1

So I have been down the rabbit hole of finding that HTML forms don't support DELETE requests, and then looking into mimicking the the behaviour with a Javascript fetch request (but it is not straightforward to implement in Javascript). The way I see it, with or without Javascript, there are 3 options available:

  1. Have a dedicated path for deleting resources For example POST /resources/delete/resource_id. This seems like the best option as it means you can have a dedicated route on the server. It is important to only accept POST requests so that users cannot accidentally delete a resource just by navigating to a URL.

  2. Adding a flag to the request data The same route is used for both posting and deleting, e.g. POST /resources/resource_id except that a flag is added to the request's data to signify whether it should be handled as a POST or DELETE. For example <input type="hidden" name="delete" />. This seems to be a common approach, and feels nice because it uses the same path that an actual delete request would, e.g DELETE /resources/resource_id. The major drawback is that if the flag is accidentally added to the request data, this would unintentionally change a POST request to a DELETE request, which is obviously very bad.

  3. Use Javascript to send an actual DELETE request This is nice because the server doesn't have to do anything different and can simply have a proper DELETE route like DELETE /resources/resource_id. This obviously makes sense if your webpage is a SPA which is already using Javascript to handle making network requests and page navigation. The downside is it is the most complex solution to implement. Also anytime you start using Javascript to mimic browser behaviour, you risk introducing different, unexpected behaviours. Below is my current best attempt which seems to work except clicking the back button to navigate to the previous page, the address bar updates but a request is not made by the browser and the previous HTML page is not rendered. Has anyone attempted something similar? Am I on the right track or is this a lost cause?

Currently my view is that option 1 is the best (easiest, most reliable and pragmatic) solution, but I am new to this and have struggled to find good resources on the web, should I would really appreciate some advice and guidance. What is common practice? What is best practice? Have I missed any other solutions? Using option 1, what form should the path take? /delete/resources/resource_id, /resources/resource_id/delete, etc.

<!DOCTYPE html>
<html lang="en">
  <head>
    <script>
      window.onload = () => {
        // find all forms with hidden value=delete input
        const forms = document.querySelectorAll("form");
        forms.forEach(form => {
          const input = form.querySelector("input");
          if (input.value == "delete") {
            form.addEventListener("submit", async e => {
              e.preventDefault();
              const response = await fetch(form.action, {
                body: new URLSearchParams(new FormData(e.currentTarget)),
                method: "DELETE",
                redirect: "follow",
              });
              const text = await response.text();
              history.pushState({}, "", response.url);
              window.document.body.innerHTML = text;
            });
          }
        });
      };
    </script>
  </head>
  <body>
    <form action="/delete" method="POST">
      <input type="hidden" value="delete" />
      <input type="submit" value="submit" />
      <input name="key" value="val" />
    </form>
  </body>
</html>
4

Due to the limited support for HTTP verbs in HTML forms, there is no right or wrong way. There is only "works" and "doesn't work."

The only guideline you should follow is to avoid a GET request. Never modify or destroy data with a GET request. If GET /resource/123 deletes something, imagine an attacker tricks a logged in user to view a web page with a bunch of <script> tags:

<script src="http://yourdomain.com/resource/123"></script>

The browser happily make cross domain requests with script tags and include all session cookies. Of course the browser pukes out trying to parse the result as JavaScript, but the request gets sent. Bye-bye data.

With HTML forms, use the easiest solution as long as it is not a GET request. Each of your proposed solutions work depending on the use case.

If the entire form represents the resource or collection of resources that should be deleted, then a POST to a single URL works just fine.

If the form is a composite of resources where you can optionally delete some of them, a hidden field representing a delete flag for each item works just fine.

If deleting a resource from the UI should physically delete the resource without POSTing the entire form back, then a DELETE triggered via Ajax is appropriate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.