In my previous article, I discussed the pros and cons of reusing HTTP verbs as REST verbs. The advantage is that you don’t have to learn a new set of verbs when you want to learn REST. The drawback is that the HTTP verbs do not always match the needs of your REST webservices. The same is true for the HTTP response codes: reusing them saves you from having to learn a new set of response codes, but the drawback is a mismatch that you will have to compensate for in your REST API.
Let’s start with the most common HTTP response codes. When you’re surfing on the Internet with your browser, the most frequent response code you’ll encounter will probably be 200 OK. Normally you won’t see it, because it indicates that all went fine (like when you loaded this page), and it’s therefore hidden by your browser. A response code people may be more familiar with is 404 Not Found, when you try to open a page that doesn’t exist (e.g. because you mistyped its URL). In many cases, the browser just displays a standard message, but some sites take pride in creating a special page with some art on it, like e.g. GitHub‘s Page not found page.
When you look at the list of HTTP response codes, it’s easy to become overwhelmed. Fortunately, not all of them are frequently used, and one of them is actually just a joke. But if you take a closer look at the list, you’ll notice that many of the HTTP response codes aren’t relevant to REST at all. Take e.g. 417 Expectation Failed: I have a hard time imagining a situation where a REST webservice should actively respond with that response code.
Therefore, the set of response codes that are explicitly used by REST webservices is rather short. I would say that the aforementioned 200 OK and 404 Not Found, together with 401 Unauthorized and 403 Forbidden would be the most common ones. 201 Created should be returned when a new resource is created, but beginners will often make the mistake of returning a 200 OK instead. The same happens a lot with 202 Accepted, and 204 No Content.
When the payload of a REST call doesn’t conform to the XSD, e.g. because of syntactical problems, the call should be rejected with a 400 Bad Request response. Often, that’s already taken care of by the infrastructure if you chose a good REST webservices framework or platform. But what if there’s a semantic problem with the payload? Not all constraints on the content of the call can be expressed in the XSD and therefore handled by the XML validation in the REST interface.
Say you have a REST service to create a new resource of type Foo, and somewhere in the content of the call, there should be a reference to an already existing resource of type Bar. Clearly, this constraint can’t be baked into the XSD, because then it would have to be updated and propagated to all clients every time a Bar resource is created or deleted. So what’s the response code when the reference to a Bar resource is present and well-formed, but not pointing to an actually existing Bar resource? Responding with a 400 Bad Request is incorrect, because the syntax of the message is fine. One could think that maybe a 404 Not Found would be appropriate, since the Bar resource couldn’t be found. However, a 404 Not Found should be used when the requested resource couldn’t be found, not one of the resources referenced to in the payload. And a 500 Internal Server Error isn’t correct either, because we’re not dealing with an internal server error–we’re actually preventing one from happening later on.
The best solution that I know of is to use the response code 422 Unprocessable Entity, with an error message explaining the cause of the problem. Wikipedia’s explanation of the response code says that it should be used when “[t]he request was well-formed but was unable to be followed due to semantic errors”, and that sums up our situation quite nicely. Other situations in which I’ve used a 422 Unprocessable Entity include errors due to illegal values that couldn’t or shouldn’t be encoded in the XSD (e.g. an incorrect ZIP code), an invalid combination of choices or resources (e.g. trying to register a marriage with two times the same person), or anything else that doesn’t make sense one way or the other (e.g. trying to register a man as the biological mother of a child).
Once you start working with REST webservices, you’ll find out quickly that many exceptions part of the business logic of your application will resolve into a 422 Unprocessable Entity in your REST API. That’s a bit ironic, because one of the HTTP response codes you’ll be dealing with the most, wasn’t even part of the original set of HTTP response codes, but added later on by WebDAV, an extension of HTTP. But what’s more serious is that you’ll need to create an error message type to go with it, in order to explain what really went wrong. Clients will have to inspect that error message, so they can decide what to do next. But wait a minute, wasn’t the use of HTTP response codes supposed to save us from doing exactly that, namely creating our own set of exception codes and sending them to clients as the payload of a response?