1. 45
  1.  

  2. 20

    Even if they didn’t make sense as form elements, it sucks when you have a RESTy API and don’t want to involve JS on the frontend. I’d love to make a link or form’s method DELETE, but instead, I have to implement something dumb like GET /object/3/delete or something.

    1. 9

      I have to implement something dumb like GET /object/3/delete or something.

      Please remember that GETs are defined as safe and idempotent methods. A GET that deletes an object violates this.

      This isn’t a theoretical concern, either. I’ve been professionally burnt by misbehaving middleboxes replaying traffic, web spiders and browsers prefetching endpoints that mutate state… so make your non-read only endpoints accept form POSTs instead.

      1. 6

        I understand what you mean, and I agree it would be helpful to have other methods supported, but using a GET is absolutely the wrong work around. A DELETE or PUT request should be handled by most layers (browsers, proxies/caches, app servers) the same way a POST should - the semantic difference is not relevant for those layers.

        Edit to Add: the only valid reason I can see for a “GET” response to a ‘/delete’ endpoint like that, is to show a prompt to confirm deletion of something (i.e when not relying on JS, or when relying on simple XHR and very little front-end logic to do the confirmation. ie. GET /foo/bar/delete shows a confirmation, which is a form to submit POST /foo/bar/delete.

        1. 5

          One way to get around this is adding a hidden method field to your form and then having your backend look for that. This is what Ruby on Rails does for example.

          It’s kinda silly. I don’t understand the objections against just adding it to the form element.

          1. 6

            Best explanation I’ve seen is “browser vendors are why we can’t have nice things”; same as client TLS certs.

            1. 1

              There is no literal objection. It just hasn’t been figured out yet. I just submitted a top-level comment to explain the issues that would need to be resolved. With Rails, the website is obviously opting in by implementing this convention in frontend and backend explicitly. But that Rails only covers the same-origin case AFAIU.

              1. 2

                Those weren’t the cited reasons though, which were mostly fairly dismissive. There is a specification, which addresses CORS, but I can’t readily find why that wasn’t implemented.

            2. 2

              For precisely that purpose, I always use the Rails UJS library.

              I use it without Rails. It’s a minimal amount of JavaScript that will save you a lot of time, as it does exactly what you mentioned in your comment.

              1. 2

                Frameworks like Laravel use a ‘magic’ _method field, which you can use to override the request method.

                <input type="hidden" name="_method" value="DELETE" />

                It’s not ideal, but it makes handling it in middleware to rewrite the request okay, without having to mess with other routes.

                1. 1

                  If what you want is the convenience of calling the right method without the pain of dealing it in JavaScript - as opposed to JS being a hard no - then I couldn’t recommend the htmx library enough. I’ve been using it for a few weeks in a new project and it’s ability to get rid of boilerplate JS code is unsurpassed.

                2. 12

                  There are some interesting CSRF security issues that need to be solved before this can be a thing: Currently, forms are exempt from CORS-checks due to being older than CORS: And any request of type POST or GET can always be sent cross-origin with a form. But all other requests will require a positive a CORS pre-flight request before they can be sent.

                  So, this assertion needs to hold true for for DELETE and PUT requests (due to CSRF-concerns). In essence, this means someone would have to specify and figure out the processing logic to wire CORS checks (i.e., pre-flight requests) into a form submission. This is tricky because form submissions are different types of requests than those using XMLHttpRequest/fetch. Specifically, because a form submissions is a page navigation.

                  1. 3

                    CRSF checks are normally handled with a security token that is included as a hidden input in the form. This works for POST and GET requests so why couldn’t it work for DELETE and PUT requests? Sorry if I’m missing your point.

                    1. 1

                      What you are talking about is in the Webapps. The browser has some CSRF protections too. And that’s CORS.

                  2. 7

                    The actual rejection comment of Hickson has a good point about DELETE: you usually don’t want a body.

                    But for PUT? Nothing except that “you wouldn’t want to PUT a form payload.” that’s a quote weak argument.

                    1. 10

                      It’s no problem to make a form that sends no body though…

                      1. 6

                        The spec says delete MAY have body. And practically speaking, you’d always want a csrf token anyway. I didn’t understand the PUT argument at all – not that it was weak, I simply didn’t understand what he was arguing – and posted another question in this thread about it.

                        1. 3

                          How is that a “good point”?

                          1. 1

                            I wanted to be somewhat generous as in if you write an API, many delete requests don’t have bodies.

                            But the sibling comment about the CSRF token is a good one.

                        2. 3

                          The bug was eventually closed as Won’t Fix by Ian Hickson, with the following rationale (href):

                          PUT as a form method makes no sense, you wouldn’t want to PUT a form payload. DELETE only makes sense if there is no payload, so it doesn’t make much sense with forms either.

                          Can someone explain his argument? I looked up the original email thread and there was no more context. Why wouldn’t you want to PUT a form payload but POSTing one is ok? Additionally, why is this situation different from PUTing a JSON payload to an API endpoint? Afaict the only different is the representation – application/x-www-form-urlencoded vs application/json. What am I missing?

                          1. 2

                            Not just these two, but all methods. Why in the world can I not <form method="buy"> after all these years is baffling. This is a huge contributing factor to people giving up on HTTP, when browsers refuse to implement it sensibly.

                            1. 3

                              Assuming you mean adding custom methods to HTTP, how would a browser know which of these methods are idempotent?

                              I don’t think the lack of custom methods harms HTTPs usage personally, though this may be because I’ve internalized a record-cetric way of modelling. I would probably implement that as a POST /products/123/purchases, or maybe PUT /products/123/purchases/<transaction_id>

                              1. 4

                                No custom method is idempotent, basically. That’s the only safe way

                                1. 1

                                  Provide an attribute on the form which tells the browser if the request is idemptotent.

                                  A POST can be idempotent too, but the browser assumes it is not.

                                2. 3

                                  This feature would contradict the basic design principles of HTTP. The spec has this to say on the HTTP verbs:

                                  HTTP was originally designed to be usable as an interface to distributed object systems…

                                  Unlike distributed objects, the standardized request methods in HTTP are not resource-specific, since uniform interfaces provide for better visibility and reuse in network-based systems [REST]. Once defined, a standardized method ought to have the same semantics when applied to any resource, though each resource determines for itself whether those semantics are implemented or allowed.

                                  Fielding also addresses this point:

                                  The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components (Figure 5-6). By applying the software engineering principle of generality to the component interface, the overall system architecture is simplified and the visibility of interactions is improved. Implementations are decoupled from the services they provide, which encourages independent evolvability. The trade-off, though, is that a uniform interface degrades efficiency, since information is transferred in a standardized form rather than one which is specific to an application’s needs.

                                  That is, the basic idea is that every application has resources, identified by URIs, that you interact with via this tiny set of very general verbs. The specifics of your application are not defined by adding custom verbs, but by custom media types. This article walks through a simple example of doing this.

                                  A whole world-view is implied here: The world is made up of resources, each with a canonical location. You can view them, create them, update them, and delete them. Want to log in? Wrong. You are actually creating a new session resource. Want a refund on your purchase? Create a new refund request resource. It is a noun-centric world view.

                                  The merits of this design are debatable, of course, but understanding the intention will at least make things less confusing, and you don’t have to wonder why this feature hasn’t been implemented yet.

                                  1. 1

                                    The noun-centric worldview is precisely why GET and POST are insufficient. There are things you can do with a resource besides get it or create it.

                                    1. 2

                                      This is still verb-think :)

                                      As another poster mentioned: In your example, you don’t buy something – you create a new purchase resource (or order, or whatever). Yes, this is unnatural. It clearly doesn’t click for most people. Probably it was not the best design. Still, it is absolutely fundamental to the principles of REST and HTTP, and I think it’s easier to just embrace it. I don’t think it’s going anywhere.

                                3. 2

                                  The question presumes a burden to argue against a feature that doesn’t exist. Surely the burden is on thise who would add a feature to argue that it’s required or at least cost-effective.

                                  The first form impl didn’t support any attrs on the form elt, iirc. Then action was added so that the page could be at a different url than the query service. POST was originally motivated by limitations on URL / path lengths in servers, if I’m not mistaken…

                                  Stepping back, POST is in some ways complete by itself (think of it as INVOKE rather than INSERT). GET is an optimization for caching and such.

                                  What’s the argument for PUT or DELETE?

                                  1. 1

                                    Because those methods are for astronaut architects, and the only two methods you should ever use in reality are GET and POST. If you’re using Rails and it comes with PUT and DELETE specified, okay, I guess don’t go out of your way to redefine them, but the other verbs are pointless. There are many ways of communicating something to an HTTP server:

                                    • the URL path
                                    • query parameters
                                    • headers
                                    • the request body (for post)

                                    You don’t need yet a fifth way of communicating random bits of information. Use GET if it’s idempotent. Use POST if it is not idempotent. That’s a well defined semantic. DELETE in most cases should mean “set the is-hidden flag to true” anyway, which is a POST. PUT means even less than DELETE.

                                    1. 2

                                      A POST can often be idempotent. A PUT should almost never be idempotent. This is a useful distinction in many cases and especially so when there is middleware involved. There are an unknown number of firewalls and other services between your browser and your client. They make decisions based on the semantic meanings of these methods and pretending they aren’t there will lead to odd bugs eventually that are difficult to track down.

                                      1. 2

                                        An operation being potentially mutative, rather than idempotent, is perhaps a more appropriate consideration here. Mutative operations can still be idempotent of course, something like

                                        func (a *Account) SetBalance(target int) {
                                          a.currentBalance = target
                                        }
                                        

                                        GET requests should not be mutative under any circumstances, while POSTs are (although they can be implemented idempotently)

                                        1. 1

                                          What middleware is going to replay a POST request? POST response, maybe, but a request?

                                          1. 1

                                            It’s the reverse:

                                            A POST can often be idempotent.

                                            https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST

                                            A PUT should almost never be idempotent.

                                            https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT

                                            1. 1

                                              You are correct. I transposed them in my head apparently. The larger point still stands though.

                                          2. 1

                                            Because those methods are for astronaut architects

                                            Most major APIs (eg, github, etc) use PUT and DELETE. I’m not against criticisms of the whole paradigm, but these distinctions in practice are completely standard in the industry and not the special domain of “astronaut architects”.