[HN Gopher] Request bodies in GET requests
___________________________________________________________________
Request bodies in GET requests
Author : treve
Score : 106 points
Date : 2022-01-29 19:37 UTC (2 days ago)
(HTM) web link (evertpot.com)
(TXT) w3m dump (evertpot.com)
| lhnz wrote:
| This brings back memories. I once failed an interview because an
| interviewer asked me how a certain API could be implemented and I
| suggested that you could send a body with a GET request. My
| thinking at the time was that, even though unusual, Elasticsearch
| does this (2016:
| https://github.com/elastic/elasticsearch/issues/16024#issuec...)
| and it might be easier to cache than POST but I could tell that
| the interviewer wasn't happy with my reponse.
|
| Glad that a QUERY method will be provided instead, and I learnt
| my lesson about giving against-the-grain responses to
| interviewers.
| gautamdivgi wrote:
| The QUERY method is definitely the way to go. GET with a body
| is iffy. If you owned the entire stack it might be doable. But
| once you don't have control of the many http proxies and
| firewalls that a GET with a body has to travel then it becomes
| a problem. I think the RFC does not explicitly define the
| behavior of GET with a body. So, proxies or firewalls can just
| drop the message and be completely HTTP compliant.
|
| Not passing judgement - you may have very well qualified your
| statement. But having dealt with the above issue some years
| back (as in being the person on the debugging run) I do
| sympathize with the unhappiness. :)
| lhnz wrote:
| Yeah, your point about needing to own the entire stack in
| this case makes perfect sense, and to be honest, I didn't
| qualify what I said well at all at the time. When I left the
| interview I had that sense of "damn, why did I say something
| so strange with no explanation of where I'd seen this or
| argument against it". There could have been a conversation
| about it, but my response just made the interviewer tick the
| 'no' box.
| chrsig wrote:
| I get so exhausted listening to people talk about http semantics,
| and arguing about the restfulness of different approaches.
|
| From an implementation point of view, the difference between a
| POST, GET, and QUERY with a body is...trivial. I look forward to
| QUERY because it'll finally stop arguing about having to send a
| body on GET, or do a POST for something like a search.
|
| It's gotten to the point where it all feels like naval gazing to
| me. I'd rather talk about what the api does rather than how
| amazingly restful it is.
| dylan604 wrote:
| At the end of the day, I don't care. If the API I am talking to
| says they respond to certain information sent via specific
| method, that's how I send it. Yes, we could argue till the cows
| come home about levels of appropriateness, but I've got better
| things to do with that time. Like process the data I just
| received from GET request sent with a body!
| CodesInChaos wrote:
| I think "REST" was primarily successful because people wanted
| a simple json-webapi, not because of its fancier principles.
| polishdude20 wrote:
| Exactly. If I need to access some api, I'll just read their
| docs on which methods they want you to use. I never go in
| thinking "oh if there's a get request for this resource,
| there must be a post request for this resource like this".
| hakre wrote:
| Actually I would test the request first. And only if it
| doesn't work this would require the documentation, so would
| then test it after the functional test. If the
| documentation is even needed.
| pacoverdi wrote:
| You'd like my APIs, I keep defining DELETE endpoints with
| request bodies even though OpenAPI tools scream warnings at
| me :)
|
| Doing a POST on a /delete endpoint seem unnatural and I
| prefer serializing complex objects to JSON rather than to a
| query string.
| toyg wrote:
| _> it all feels like naval gazing_
|
| What did those poor ships do to you? /s
|
| It's nav _el_ gazing, as in "looking at your belly-button":
| https://en.wikipedia.org/wiki/Navel
| chrsig wrote:
| They said everything's going into containers on on their way
| to the clouds /s
|
| Thank you for the correction, I had a good chuckle.
| cryptonector wrote:
| > I get so exhausted listening to people talk about http
| semantics, and arguing about the restfulness of different
| approaches.
|
| I'm with you this far.
|
| > From an implementation point of view, the difference between
| a POST, GET, and QUERY with a body is...trivial.
|
| Sadly the difference is not trivial at all. I suppose if you're
| implementing an application-specific HTTP server that directly
| understands the application's semantics then the difference
| between these is trivial enough. But for the _client_ the
| difference is not trivial at all, and the clients are bound to
| be off-the-shelf in many cases. And for an off-the-shelf HTTP
| server framework the difference may not be so trivial either.
| chrsig wrote:
| I've stared at enough http where the only difference to me is
| GET /foo HTTP/1.1\rnHost: example.com\rn\rn{"foo":"bar"}
| POST /foo HTTP/1.1\rnHost: example.com\rn\rn{"foo":"bar"}
| QUERY /foo HTTP/1.1\rnHost: example.com\rn\rn{"foo":"bar"}
|
| If it's a possibility that potential clients might refuse to
| do something like a GET with a body...don't use that as part
| of your api. Or make the endpoint respond identically to a
| GET with a body as a POST with a body. Similarly if you're
| working in a http server framework that makes such a thing
| difficult.
|
| At the end of the day, I care a hell of a lot more about how
| well the api is documented than I do about any of it's
| particular semantics.
| e12e wrote:
| > From an implementation point of view, the difference between
| a POST, GET, and QUERY with a body is...trivial.
|
| I'm quite certain that if I were implementing a caching proxy
| (or a caching client, like a browser) - I'd find the diffence
| rather important. That said, I'm also sure I'd have to consider
| special exceptions for popular software that got the details
| wrong.
|
| And if you're not caching, why use http anyway?
| ok_dad wrote:
| Imagine saying something like:
|
| > I get exhausted by people talking about integers, floats, and
| strings and arguing about the "types" of different things. From
| an implementation point of view, the difference between an
| integer, an float with or without a mantissa, or a string with
| numbers and decimal symbols is trivial. It has gotten to the
| point that...
|
| Things have meanings, and in computer science and programming
| those meanings can mean a performace difference or a difference
| in results, so I think it is _very_ important that we start to
| pay attention to the "naval gazing" aspects of these things
| and not use a GET with a body, since there is a QUERY, and
| other specs which might be kinda naval-gazey.
|
| We are in a career that requires a pretty high level of
| strictness and we're always complaining about bugs in this or
| that software; then we argue that there is no meaning in
| certain things (only because in practice, people hadn't been
| strict about something like a GET request) which then leads to
| more bugs!
| zestyping wrote:
| Thank you.
| ogazitt wrote:
| Seems like the HTTP/2 protocol has been baked for so long, that
| whatever new methods are added, adoption will be very slow and
| only a small fraction of web APIs will expose those methods.
|
| In other words, aside from <1% of developers, no one will care.
|
| Also, graphQL seems to address many of the issues that manifest
| in the current form of GET. My bet will be that graphQL gets
| adopted more widely (and faster) than a new QUERY method.
| yob22 wrote:
| jicea wrote:
| I've to build a cli HTTP client in Java and went with OkHttp [1],
| as it was widely used on Android. I've to add support for GET
| requests with a body but I wasn't able to do it with OkHttp. The
| library was rather opinionated and you couldn't add a body to a
| request body back then [2]. I was rather surprised, because I
| thought HTTP specs allowed it, while discouraging; so an HTTP
| library should allow this kind of usage. I went back to Apache
| HTTPComponents, while less fancy than OkHttp but also less
| opinionated, and was able to complete my client. Things may have
| changed, it was a few years ago.
|
| [1] https://github.com/square/okhttp
|
| [2] https://github.com/square/okhttp/issues/5803
|
| [3] https://hc.apache.org/
| bruce511 wrote:
| Standards are nice and all, but I've implemented a generic Web-
| client library and had to support payloads with all HTTP Verbs,
| because, well, some servers require it.
|
| Clients of a service seldom get to meaningfully complain - use
| the service, don't use the service, whatever... So whatever the
| server wants, the server gets...
|
| Although ironically I've also implemented servers and Gorton all
| kinds of complaining from clients - it's too hard to set a
| cookie, or header, can't we put tokens inside the JSON etc...
| hyperhopper wrote:
| Yeah, nobody is saying start using QUERY today for all your
| requests, it's a suggestion to design new apis with more
| semantic verbs that follow the spec.
| balls187 wrote:
| Put me in the column of being an ardent standards supporter.
|
| I really don't like buying a[n] (HDMI|USB|Lightning) cable and
| then finding out it's not standards compliant, so my
| (PS5|PC|Phone) doesn't work.
|
| I'm old enough to remember Web-development with Internet
| Explorer, and so old, I remember cross platform C++ development
| using Visual C++.
| sseagull wrote:
| I took a deepish dive into these kinds of issues. My
| interpretation is that no one uses REST properly (ie, HATEOAS).
| Fielding has even said so - if you aren't designing something as
| big as HTTP itself, then "true" REST isn't for you.
|
| Now there's this thing called REST that we all commonly
| understand and debate over. And I have run into so many issues
| that I basically abandoned calling it REST and now just call it
| an API.
|
| And somehow it still all works just as well :)
| egeozcan wrote:
| Use GET for everything that can be cached and repeated without
| side-effects, and POST for everything else.
|
| You can delete the rest of the spec and clients can connect
| without head-aches but only if you can put up with the fanatics
| in your team trying to trace every bug to not following rest,
| in the rare case giving you no options other than trying patch
| after patch to make everything rest-compliant.
| hakre wrote:
| > Use GET for everything that can be cached and repeated
| without side-effects
|
| As long as HTTP is concerned (which must not be specifically
| REST), HEAD needs to be supported next to GET, too.
| shroompasta wrote:
| True.
|
| I have personally gone off the deep end and started writing
| 200, 400, and 500 for all my status codes instead of the
| specific 2xx, 4xx, 5xx ones.
|
| If details of an error response need to be mentioned, it will
| be in the error response body.
|
| My motivation came from GraphQL as it responds everything with
| 200s, and so far it has been humming along fine.
|
| True REST are more guidelines if anything.
| reidjs wrote:
| That part of GQL drives me nuts. How the heck do people do
| error handling on the frontend with API requests through GQL?
| Instead of `try catch`, do you do `if (response.error) {}`?
| samsonradu wrote:
| Yes, can be handled with middleware (interceptors) that
| throw the `response.error`
| capableweb wrote:
| > My motivation came from GraphQL as it responds everything
| with 200s, and so far it has been humming along fine.
|
| This is slightly annoying though, as other consumers (like
| `fetch` in JavaScript world for example) behaves differently
| if there is a error. If every status is 200, suddenly you
| need to manually check the response inside the returned
| promise, while if you answer with a correct status code when
| the server encountered an error (5xx), then it'll jump to the
| `.catch` part of the promise chain instead, which you're
| probably already handling anyways.
|
| Similarly with curl if I'm not mistaken (don't have it in
| front of me). A 2xx would make the process return 0, while a
| 5xx would make it return non-0, so you can handle errors
| without having to read the actual response.
| beardedetim wrote:
| Yes. Yes Yes yes. Status code is important beyond your eyes
| and your hand-rolled api interactions.
|
| I can't believe the people here saying "fuck it, I always
| return 200"
| hakre wrote:
| That must be something fetch specific, per the HTTP specs
| IIRC, any status code should be passed to the application -
| and the way I read it - unfiltered. It is neither an error
| nor an exception, the request is successful. A DNS or
| server connection issue would not be.
|
| Similar with curl, 502 returns non-zero, 302 not. 4xx IIRC
| as well zero unless --fail is in use. The default makes it
| sensible/useful for smoke tests.
|
| Perhaps GraphQL was aware of such client side "bugs" it
| choose to go "OK" first of all.
| beardedetim wrote:
| The fact that I have to parse the body and intropsect its
| payload to know that the query is malformed is the worst
| client experience ever.
| shroompasta wrote:
| APIs should be giving back specified error messages
| regardless as 4xx and 5xx errors can still be too generic.
|
| For example, if you're rate limited (429), a robust API
| will still give you back data on how many requests you've
| made and how many requests you're allowed to make, so
| you're still going to have to check the payload regardless.
|
| The combination of specific error status codes along with
| error messages has been redundant in my experience.
|
| Furthermore, in Axios, checking for `error.response.data`
| isn't terribly far from `error.response.status`, so I'm
| unsure of this "worst client experience" you're talking
| about.
|
| It's pretty intuitive for me.
| beardedetim wrote:
| I don't want to introspect response bodies when I can
| introspect the status. If I'm given 429, I can
| automatically do something based on that. If I need to
| introspect the response body I have to say "okay, look
| inside Joe's api and look for key Foo. But in Sally's
| api, look for key Bar. And in Mike's api look for key
| Baz. And...."
|
| I think its objectively worse that I, as the engineer,
| need to handle all of them differently when they all
| could have returned to me 429 instead and I could write a
| general wrapper for that.
| shroompasta wrote:
| I"m sorry, i'm not seeing the difference
|
| how is
|
| if error.response.status === 429 // then do something
|
| different than
|
| if error.response.data.err === 'RATE_LIMITED' // then do
| something
|
| Also, do you mean to tell me that you've never had to
| elaborate on your error messages and just sent back
| status codes with no body?
|
| I'm sorry but that does not sound like a robust API to
| me.
|
| 90% of my error responses have specified data, and I have
| to check the response body extremely often regardless of
| the status code being sent back.
| Osiris wrote:
| Yeah I tend to refer to them as HTTP APIs.
| dang wrote:
| Ongoing current thread:
|
| _Upcoming new HTTP QUERY method_ -
| https://news.ycombinator.com/item?id=30153995 - Jan 2022 (52
| comments)
|
| Recent:
|
| _IETF: The HTTP Query Method ( Draft)_ -
| https://news.ycombinator.com/item?id=29794838 - Jan 2022 (125
| comments)
| SahAssar wrote:
| The first mention of QUERY should probably point to
| https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-saf...
| instead of https://datatracker.ietf.org/doc/html/draft-ietf-
| httpbis-sem...
|
| I was a bit confused by that at first
| cryptonector wrote:
| Me too. I think it's just an error by the author.
| treve wrote:
| It was! I'm fixing this
| UglyToad wrote:
| I really, really, hate REST prescriptivism. I'm going to return
| 200 from POSTs and want to use request bodies for GET requests to
| do filtering. We may as well use POST for updates. REST doesn't
| work for my use-case or probably most use-cases but people seem
| to treat it like an article of faith even when it forces a
| completely bass-ackwards design.
|
| SOAP was the pinnacle of API design.
| akvadrako wrote:
| You can't use bodies in GET requests in some popular browsers,
| so better just use POST for everything you don't want cached.
| theandrewbailey wrote:
| In my view, REST is how HTTP is supposed to be used. If you're
| going to POST and 200 everything, you're probably re-inventing
| things that HTTP already has well-tested and already
| implemented infrastructure for. If HTTP already has a clear
| solution for what you're doing, you should use that instead of
| building it yourself on top of HTTP.
| nrmitchi wrote:
| It's not even just that, but by avoiding the well-known
| standards of how HTTP behaves, you're going to find yourself
| fighting with any tool/system that is based on those
| standards.
| gls2ro wrote:
| You can do that (adding body to GET) but you might generate
| some restrictions for using the cire. Example from the article:
|
| > When working with HTTP, there's servers but also load
| balancers, proxies, browsers and other clients that all need to
| work together. The behavior isn't just undefined server-side, a
| load balancer might choose to silently drop bodies or throw
| errors. There's many real-world examples of this. fetch() for
| example will throw an error.
| UglyToad wrote:
| Sorry I wasn't clear. The lack of defined behaviour for
| bodies in GET is an example of harms arising from REST
| obsession. It's clearly a useful thing but because it goes
| against the intended design it's treated as a bad idea. It
| just feels like REST discussions are where the strongest
| divide between pragmatism and bikeshedding occur.
| cryptonector wrote:
| While REST obsession is a thing, the "lack of defined
| behaviour for bodies in GET" arises from the fact that it's
| _defined_ to not have a request body, therefore there
| exists a lot of code that assumes it has no request body
| and acts accordingly, leading to interoperability problems.
|
| You have to separate HTTP, the spec, from REST the
| philosophy.
|
| REST says you should use HTTP status codes. HTTP doesn't
| obligate you to use the full range of its status codes --
| you can code up a resource that always returns 200 for
| POSTs, and that's fine as far as HTTP goes, and not as far
| as REST goes.
| dragonwriter wrote:
| > REST says you should use HTTP status codes
|
| No, it doesn't, though leveraging the semantics of the
| underlying communications protocol (such as response
| codes when running over HTTP) is a convenient way to
| satisfying the "self-describing messages" constraint of
| REST.
|
| > you can code up a resource that always returns 200 for
| POSTs, and that's fine as far as HTTP goes, and not as
| far as REST goes.
|
| The only reason it would be even slightly problematic for
| REST is if it were inconsistent with the semantics of
| HTTP. Which, of course, returning 200 for anything but
| success would be.
| GordonS wrote:
| It's always bugged me having to use POST for complex query
| requests that have "outgrown" URL parameters. Had no idea a new
| QUERY verb was in the works - that sounds ideal.
| fxleach wrote:
| Why does it bug you?
| dylan604 wrote:
| This was the first I had heard of Query as well, but admittedly
| I don't pay too close attention to those discussions.
| Waterluvian wrote:
| Same. Also I (foolishly) perceived the spec as being rather
| stable and was not expecting a whole new verb.
| dylan604 wrote:
| There's definitely a lot of misunderstood uses of the
| verbs, and I've maybe read about them one only a couple of
| times long long ago. I know POST gets bastardized a lot in
| attempts to avoid query strings attached to the end of
| GETs. Lots of people promoting that in tutorials/blogs/etc
| treve wrote:
| QUERY seems to be on the path to standization, but using
| POST if other methods don't fit is/was the right way to
| do this!
|
| Both PATCH and QUERY are newer and they are really
| specializations of what people used to use POST for.
| toyg wrote:
| The problem with "exotic" verbs is that proxies in the middle
| will often misbehave when faced with them. There is some old
| network infrastructure out there.
| wizzwizz4 wrote:
| With HTTPS, that doesn't matter.
| mh- wrote:
| not really. there are plenty of middleboxes deployed by IT
| departments that happily mess with HTTPS traffic, too.
| tedunangst wrote:
| There are intercepting https proxies.
| jrockway wrote:
| The proxies in question strip HTTPS, and re-encrypt with a
| certificate your IT department installed on your device.
| wizzwizz4 wrote:
| Those break everything _anyway_. Nobody should be obliged
| to support that.
|
| ... Now, back to reality. :-(
| dillondoyle wrote:
| genuinely curious. why? Does it make a big difference in your
| code or user experience?
| isbvhodnvemrwvn wrote:
| Caching gets rather tricky.
| dylan604 wrote:
| Maybe because caching POST data is just an odd way of doing
| things?
| tptacek wrote:
| Yes! It can make a big difference! For instance: in a single-
| writer many-read-replica deployment, GETs might be routed
| automatically to replicas, while POSTs might have to be
| routed to the writer. You can design around it (for instance,
| route everything to replicas, catch exceptions on writes, and
| replay to the writer), but it's simpler and marginally faster
| to simply distinguish based on the verb.
| abhishekjha wrote:
| > route everything to replicas, catch exceptions on writes,
| and replay to the writer),
|
| I am curious about the specifics here. Do you do it at the
| application level or proxy level?
|
| Does doing it at the app level require that all the
| requests can be routed to any DB instance regardless of
| whether they are master or slave. A write to a mster is
| passed through. A read to a master is routed to slaves LB.
| A read on slave is passed through. A write on replica is
| routed to the master.
|
| Does this look like a sound design?
| cryptonector wrote:
| If you can only use POST, then the query is by definition not
| safe in the HTTP sense even if it would be with a QUERY
| method: POST is defined to be not-safe.
|
| So an HTTP framework would not retry, moving retry logic into
| the application.
|
| If you can only use POST, then you also can't cache the
| result because POST is not safe: you've no idea if the same
| POST is intended to produce the same response.
| nrmitchi wrote:
| You often want to cache results of complex queries.
|
| If these are POST, you find yourself trying to conditionally
| cache POST requests, which opens up a whole new class of
| caching bugs.
| NavinF wrote:
| If the queries are that long (over 2k-ish bytes), wouldn't
| you wanna write a query file with POST and then execute it
| by name with a GET?
| wtetzner wrote:
| Then you need to build infrastructure for storing
| queries. Depending in the specifics of the project, that
| may or may not be OK.
| dastbe wrote:
| while that could be preferred, here's some reasons why it
| may not be
|
| 1. pre-existing behavior. maybe queries have only started
| getting big enough to where this mattered, but all of our
| existing clients communicate the original way and won't
| migrate
|
| 2. more complex behavior on clients and services as
| you've now introduced a new stateful resource between
| client and service. should clients store query ids, or
| does the service handle idempotency? what db does the
| service use to store queries? how do we limit stored
| queries? do clients have to manage number of queries
| stored? does the service?
| cryptonector wrote:
| That takes two steps, needs authorization, and needs
| queries created that way to be stable so that when you
| execute them with GET you know what you're executing.
|
| This is a lot of application logic to add just to avoid
| adding a QUERY method. It's just too much.
| kuhzaam wrote:
| Some popular web frameworks limit the size of the query
| string
| cj wrote:
| Popular browsers limit the size of the URL (not
| specifically the query string, the whole URL). If
| frameworks limit qs size, it's most likely due to upstream
| browser limitations.
| matsemann wrote:
| CloudFront does as well. Bit us when Adyen didn't support a
| POST-workflow for our case, and at the time sent a GET
| request a thousand kilometers long instead.
| kevmo314 wrote:
| An interviewer once asked me what the difference between GET and
| POST were. My answer was along the lines of "well technically
| just four letters in the header but they're used differently" and
| the interviewer was _absolutely adamant_ that I was wrong and
| that there was some fundamental difference between GET and POST
| requests to the point that I tried numerous times to steer it
| back towards how they 're used.
|
| Eventually I gave up and just ended the interview.
| zestyping wrote:
| GET is supposed to be idempotent, and shouldn't have
| irreversible side-effects. Still a good idea, in my opinion.
| Browsers know they can repeat GET requests but shouldn't repeat
| POST requests without explicit confirmation.
|
| If I'm interviewing someone who claims to be familiar with
| HTTP, I expect them to know this. They might choose otherwise
| in specific situations for specific reasons, but they should
| know the convention.
| rmbyrro wrote:
| Having GET requests producing side-effects is not a good
| idea. Idempotent or not. Should avoid.
|
| GET requests should be used to retrieve information, they're
| supposed to be _read-only_.
|
| At most we could record like a counter or stats about the
| request, but not mutate resources internally.
| zestyping wrote:
| Yes, agreed. I was hedging because warming a cache could be
| technically considered a side-effect.
| freedomben wrote:
| I hate that. For some reason being the interviewer fills people
| with an unfounded confidence in their own knowledge. Some act
| like they're infallible in interviews. The result is that as a
| candidate you have to avoid harming their egos but at the same
| time don't contradict them. It can be like walking on egg
| shells.
|
| Last time I interviewed I pushed back but in a "are you sure?
| can we look it up?" kind of way and that seemed most palatable
| to them.
| thaumasiotes wrote:
| The name of the request is the only difference from the
| perspective of HTTP. But it's definitely worth mentioning that
| POST requests trigger a bunch of browser safety features that
| aren't triggered by GETs.
| stickfigure wrote:
| HTTP doesn't assume the existence of a web browser...
| abhishekjha wrote:
| >POST requests trigger a bunch of browser safety features
| that aren't triggered by GETs.
|
| Where do I read about these features getting triggered?
| rmbyrro wrote:
| There actually _is_ difference between the two. From the RFC
| 2616 specs [1]:
|
| > GET: "means _retrieve whatever information_ (in the form of
| an entity) is identified by the Request-URI " [2]
|
| > POST: "used to request that the origin _server accept the
| entity enclosed_ in the request as a new subordinate of the
| resource identified by the Request-URI " [3]
|
| [1]https://datatracker.ietf.org/doc/html/rfc2616/
|
| [2] https://datatracker.ietf.org/doc/html/rfc2616/#section-9.3
|
| [3] https://datatracker.ietf.org/doc/html/rfc2616/#section-9.5
___________________________________________________________________
(page generated 2022-01-31 23:00 UTC)