HTTP, GET, and fuzzy semantics
A developer asked me a seemingly obvious question today:
I have an API
GET
request that requires aJSON
body. Is that okay?
It's a good question too. It turns out the answer isn't as simple as it should be. On principle the answer is, “No, it's not cool”. A GET
is an idempotent request for a resource. You don't request a resource with a resource (what would that mean?), you request it with a resource identifier. The HTTP
spec, however, is unclear on the subject and does not explicitly disallow it (it probably should).
This developer was using the request body as a convenient way to send a complex set of parameters, as JSON
is much more useful to work with than URI
encoded components. His approach was both sane and pragmatic. But, it was also incorrect.
A few minutes of reading confirmed my take on it: GET
requests with payloads are bad mojo. But why is that, though?
Four reasons why you should avoid payloads GET requests
- It's unexpected. You are not guaranteed that all
HTTP
implementations will treat it in the same way. - The spec says so, sort of. Servers could/should ignore request bodies, as they have no semantic meaning to the request.
- It doesn't play nicely with caching. Many caching layers use the URI as the cache hash. Unless the URI is changing between requests, the payload will not change the result of the request.
- It's disingenuous. A
GET
request uses aURI
to name the resource. The request body is irrelevant to that request.
Sometimes the simplest questions are not answered by the spec.
And how does the story end?
The developer recast the GET
as a POST
. The semantic of the request was to retrieve a customized resource based on specific parameters, essentially creating a new resource on the fly based on the given parameters. As the filters were non-trivial, they could not be cast as HTTP
headers or components of the URI
. This was a pragmatic fix too (one line in his tool chain).