GET
, with a body!?
Specification-wise you could, but, it's not a good idea to do so injudiciously, as we shall see.
RFC 7231 §4.3.1 states that a body "has no defined semantics", but that's not to say it is forbidden. If you attach a body to the request and what your server/app makes out of it is up to you. The RFC goes on to state that GET can be "a programmatic view on various database records". Obviously such view is many times tailored by a large number of input parameters, which are not always convenient or even safe to put in the query component of the request-target.
The good: I like the verbiage. It's clear that one read/get a resource without any observable side-effects on the server (the method is "safe"), and, the request can be repeated with the same intended effect regardless of the outcome of the first request (the method is "idempotent").
The bad: An early draft of HTTP/1.1 forbade GET to have a body, and - allegedly - some implementations will even up until today drop the body, ignore the body or reject the message. For example, a dumb HTTP cache may construct a cache key out of the request-target only, being oblivious to the presence or content of a body. An even dumber server could be so ignorant that it treats the body as a new request, which effectively is called "request smuggling" (which is the act of sending "a request to one device without the other device being aware of it" - source).
Due to what I believe is primarily a concern with inoperability amongst implementations, work in progress suggests to categorize a GET body as a "SHOULD NOT", "unless [the request] is made directly to an origin server that has previously indicated, in or out of band, that such a request has a purpose and will be adequately supported" (emphasis mine).
The fix: There's a few hacks that can be employed for some of the problems with this approach. For example, body-unaware caches can indirectly become body-aware simply by appending a hash derived from the body to the query component, or disable caching altogether by responding a cache-control: no-cache
header from the server.
Alas when it comes to the request chain, one is often not in control of- or even aware, of all present and future HTTP intermediaries and how they will deal with a GET body. That's why this approach must be considered generally unreliable.
But POST
, is not idempotent!
POST
is an alternative. The POST request usually includes a message body (just for the record, body is not a requirement, see RFC 7230 §3.3.2). The very first use case example from RFC 7231 (§4.3.3) is "providing a block of data [...] to a data-handling process". So just like GET with a body, what happens with the body on the back-end side is up to you.
The good: Perhaps a more common method to apply when one wish to send a request body, for whatever purpose, and so, will likely yield the least amount of noise from your team members (some may still falsely believe that POST must create a resource).
Also, what we often pass parameters to is a search function operating upon constantly evolving data, and a POST response is only cacheable if explicit freshness information is provided in the response.
The bad: POST requests are not defined as idempotent, leading to request retry hesitancy. For example, on page reload, browsers are unwilling to resubmit an HTML form without prompting the user with a nonreadable cryptic message.
The fix: Well, just because POST is not defined to be idempotent doesn't mean it mustn't be. Indeed, RFC 7230 §6.3.1 writes: "a user agent that knows (through design or configuration) that a POST request to a given resource is safe can repeat that request automatically". So, unless your client is an HTML form, this is probably not a real problem.
QUERY
is the holy grail
There's a proposal for a new method QUERY
which does define semantics for a message body and defines the method as idempotent. See this.
Edit: As a side-note, I stumbled into this StackOverflow question after having discovered a codebase where they solely used PUT
requests for server-side search functions. This were their idea to include a body with parameters and also be idempotent. Alas the problem with PUT is that the request body has very precise semantics. Specifically, the PUT "requests that the state of the target resource be created or replaced with the state [in the body]" (RFC 7231 §4.3.4). Clearly, this excludes PUT as a viable option.
GET http://host/customer?socialsecuritynumber={ssn}
becamePOST http://host/customer/lookup
with the sensitive information sent in the body, which is excluded from external and internal logging – DistractGET
has a strong semantic meaning, and it's not 'a read operation'. It was a standard well before you tried to misuse it =). Why do you want to useGET
? – TrapsSEARCH
andQUERY
). – TrapsQUERY
http method, require a request header that must be the (sha256?) hash of the request, and then reply withVary: Your-Hash-Header
. This ensures that clients that support cachingQUERY
will be storing different cache entries for different bodies. I think there's a standard in the works for body hashes, but can't find it right now. – TrapsGET
request in myOPTIONS
responses from the server side and adjust my request accordingly. The server could read this information from a configuration file with all the other information about it's environment or it can periodically send a get request to itself with a body in it. Caching should be solved by keeping a hash in the request uri or in a header. Proxy awareness should be the front end's responsibility. – CholecalciferolSHOULD
,SHOULDN'T
etc. are defined, technical terms in these specs, they're not being passive aggressive when they say youMAY
do something. The specs are clear, you can have a body but nobody has to take it into account. That's exactly what i am trying to comply with here. My reasons include but are not limited to being able to make the distinction betweentrue
and"true"
orfalse
,null
,"false"
,undefined
,""
,{}
and[]
. – CholecalciferolQUERY
method instead. It's perfect for this, and no weird workarounds withOPTIONS
or risk of intermediates dropping the body. – TrapsQUERY
is not nearly as widely supported asGET
with a body.fetch()
appears to be an exception to this but as far asfetch()
is concerned a method namedGETBUTWITHABODY
is as valid asQUERY
. I can't seem to find what browsers will cache aQUERY
request's response but i don't think many will do. Besides I don't see many back end frameworks supporting it either. I would love to switch toQUERY
but not yet. My workaround currently seems to be more reliable. – CholecalciferolPOST
orQUERY
, notGET
bodies. – Traps