HTTP Status Codes – Should They Describe Application Level Events?

client-servererror handlinghttpserver

Several servers I have dealt with will return HTTP 200 for requests that the client ought to consider a failure, with something like 'success : false' in the body.

This does not seem like a proper implementation of HTTP codes to me, particularly in cases of failed authentication. I have read HTTP error codes pretty succinctly summed up as, '4xx' indicates that the request should not be made again until changed, while '5xx' indicates that the request may or may not be valid and can be retried, but was unsuccessful. In this case 200: login failed, or 200: couldn't find that file, or 200: missing parameter x, definitely seem wrong.

On the other hand, I could see the argument being made that '4xx' should only indicate a structural issue with the request. So that is proper to return 200: bad user/password rather than 401 unauthorized because the client is permitted to make the request, but it happens to be incorrect. This argument could be summarized as, if the server was able to process the request and make a determination at all, the response code ought to be 200, and it's up to the client to check the body for further information.

Basically, this seems to be a matter of preference. But that is unsatisfying, so if anyone has a reason why either one of these paradigms is more correct, I would like to know.

Best Answer

Interesting question.

Basically, we can reduce this down to the right way to classify things in terms analogous to OSI layers. HTTP is commonly defined as an Application Level protocol, and HTTP is indeed a generic Client/Server protocol.

However, in practice, the server is almost always a relaying device, and the client is a web browser, responsible for interpreting and rendering content: The server just passes things on to an arbitrary application, and that applications sends back arbitrary scripts which the browser is responsible for executing. The HTTP interaction itself--the request/response forms, status codes, and so on--is mostly an affair of how to request, serve, and render arbitrary content as efficiently as possible, without getting in the way. Many of the status codes and headers are indeed designed for these purposes.

The problem with trying to piggyback the HTTP protocol for handling application-specific flows, is that you're left with one of two options: 1) You must make your request/response logic a subset of the HTTP rules; or 2) You must reuse certain rules, and then the separation of concerns tends to get fuzzy. This can look nice and clean at first, but I think it's one of those design decisions you end up regretting as your project evolves.

Therefore, I would say it is better to be explicit about the separation of protocols. Let the HTTP server and the web browser do their own thing, and let the app do its own thing. The app needs to be able to make requests, and it needs the responses--and its logic as to how to request, how to interpret the responses, can be more (or less) complex than the HTTP perspective.

The other benefit of this approach, which is worth mentioning, is that applications should, generally speaking, not be dependent upon an underlying transport protocol (from a logical point of view). HTTP itself has changed in the past, and now we have HTTP 2 kicking in, following SPDY. If you view your app as no more than an HTTP functionality plugin, you might get stuck there when new infrastructures take over.