Use the Correct HTTP Method in Your XHRs

The HTTP/1.1 standard includes the following HTTP methods.

  • GET
  • HEAD
  • POST
  • PUT
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE

HTML form elements only allow the GET and POST methods. This is still true even in the HTML5 specification. The HTML5 specification authors temporarily added other methods to the HTML5 specification but later removed them.

Long ago, regular browser GET requests to load pages and HTML form GET and POST requests were the only ways web browsers were communicating with web servers. As a result, servers were implemented to support only the GET and POST methods. Send a request using a different HTTP method and you’d be taking your chances.

The Ruby on Rails community hopped on the REST train sometime around 2007 and their evangelism forever changed for the better the way web developers design their server APIs. Interest was high for using the PUT and DELETE methods, in particular, so that the four basic CRUD operations could be performed.

  • Create = POST
  • Read = GET
  • Update = PUT
  • Delete = DELETE

It wasn’t so easy to just starting to use the desired HTTP methods.

Progressive enhancements was a theme of the day. Web pages needed to work with JavaScript disabled. If JavaScript was disabled, regular HTML forms needed to work and that meant being limited to only GET and POST methods. If your server-side delete API was built to expect the DELETE method, then there was no way for a JavaScript-disabled browser to send the appropriate delete request. So the server needed to also be ready for a POST request. We ended up “tunnelling” the HTTP method through a POST request using a hidden parameter _method which, if present, the server would use instead of the actual HTTP method of the request. In an address book app, a form to delete a contact might look like the following.

<form action="/contact/537" method="POST">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="Delete">
</form>

Which would generate the HTTP request.

POST /contact/537 HTTP/1.1
Host: contact.me

_method=DELETE

The above request uses POST which is not at all what the POST method is intended to signify. POST is intended to signify that a new resource is to be created on the server. The opposite of what the request actually does.

If you are building a Web 1.0 site today, you still need to resort to the above hacking.

If you are building a JavaScript-rich, browser-based app then read on.

When JavaScript is enabled, we are able to use an XMLHttpRequest to send requests to the server. In the case of the delete example above, we can use the DELETE method directly in our request.

var xhr = new XMLHttpRequest();
xhr.open('DELETE', '/contact/537', true);
xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
        if (xhr.status == 200) {
            // do something successful
        } else {
            // handle errors
        }
    }
};
xhr.send();

Which would generate the HTTP request.

DELETE /contact/537 HTTP/1.1
Host: contact.me

This is the HTTP request we want to send but, as is often the case, the web of 2007 beat our good intentions painfully down into the ground. Sadly the web servers were not implemented to handle the DELETE method. A developer could update his own web server but had no control over the proxy web servers between the web browser and his own web server. Back to the tunnelling hack...

var xhr = new XMLHttpRequest();
xhr.open('POST', '/contact/537', true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
        if (xhr.status == 200) {
            // do something successful
        } else {
            // handle errors
        }
    }
};
xhr.send("_method=DELETE");

This results in the same undesirable HTTP request as the HTML form submission example above.

POST /contact/537 HTTP/1.1
Host: contact.me

_method=DELETE

It usually takes a long time for the web to upgrade. It especially takes a long time for users to upgrade their web browsers. It was pleasantly surprising how quickly web servers were upgraded to support more HTTP methods.

Since 2012 or even earlier, I’ve been using the PUT and DELETE method directly with XMLHttpRequest. In 2014, we started using the newer PATCH method for partially updating a resource on the server. As of 2016, with tens of thousands of users and millions of server requests, we have never had even a single bug report that could be attributed to our direct use of these HTTP methods with XMLHttpRequest.

If you have been using the HTTP method tunnelling hack with XMLHttpRequest patiently waiting for the day the web was ready, as far as I know, you can stop the tunnelling without any worry. The web of 2016 is plenty ready and capable.

If you have any experience contradicting this recommendation, please let me know.

Comments

Have something to write? Comment on this article.