Which status code should I use for failed validations or invalid duplicates?

Viewed 663674

I am building an application with a REST-based API and have come to the point where I am specifying status codes for each requests.

What status code should i send for requests failing validation or where a request is trying to add a duplicate in my database?

I've looked through http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html but none of them seems right.

Is there a common practice when sending status codes?

9 Answers

200

Ugh... (309, 400, 403, 409, 415, 422)... a lot of answers trying to guess, argue and standardize what is the best return code for a successful HTTP request but a failed REST call.

It is wrong to mix HTTP status codes and REST status codes.

However, I saw many implementations mixing them, and many developers may not agree with me.

HTTP return codes are related to the HTTP Request itself. A REST call is done using a Hypertext Transfer Protocol request and it works at a lower level than invoked REST method itself. REST is a concept/approach, and its output is a business/logical result, while HTTP result code is a transport one.

For example, returning "404 Not found" when you call /users/ is confuse, because it may mean:

  • URI is wrong (HTTP)
  • No users are found (REST)

"403 Forbidden/Access Denied" may mean:

  • Special permission needed. Browsers can handle it by asking the user/password. (HTTP)
  • Wrong access permissions configured on the server. (HTTP)
  • You need to be authenticated (REST)

And the list may continue with '500 Server error" (an Apache/Nginx HTTP thrown error or a business constraint error in REST) or other HTTP errors etc...

From the code, it's hard to understand what was the failure reason, a HTTP (transport) failure or a REST (logical) failure.

If the HTTP request physically was performed successfully it should always return 200 code, regardless is the record(s) found or not. Because URI resource is found and was handled by the HTTP server. Yes, it may return an empty set. Is it possible to receive an empty web-page with 200 as HTTP result, right?

Instead of this you may return 200 HTTP code with some options:

  • "error" object in JSON result if something goes wrong
  • Empty JSON array/object if no record found
  • A bool result/success flag in combination with previous options for a better handling.

Also, some internet providers may intercept your requests and return you a 404 HTTP code. This does not means that your data are not found, but it's something wrong at transport level.

From Wiki:

In July 2004, the UK telecom provider BT Group deployed the Cleanfeed content blocking system, which returns a 404 error to any request for content identified as potentially illegal by the Internet Watch Foundation. Other ISPs return a HTTP 403 "forbidden" error in the same circumstances. The practice of employing fake 404 errors as a means to conceal censorship has also been reported in Thailand and Tunisia. In Tunisia, where censorship was severe before the 2011 revolution, people became aware of the nature of the fake 404 errors and created an imaginary character named "Ammar 404" who represents "the invisible censor".

Why not simply answer with something like this?

{
  "result": false,
  "error": {"code": 102, "message": "Validation failed: Wrong NAME."}
}

Google always returns 200 as status code in their Geocoding API, even if the request logically fails: https://developers.google.com/maps/documentation/geocoding/intro#StatusCodes

Facebook always return 200 for successful HTTP requests, even if REST request fails: https://developers.facebook.com/docs/graph-api/using-graph-api/error-handling

It's simple, HTTP status codes are for HTTP requests. REST API is Your, define Your status codes.

I came to the conclusion to try to follow this rule: If it indicates the client developer error, go with 400 (Bad Request). Otherwise, supposing no client validation at all, if a dumb user is entering invalid data, then use 422 (Unprocessable Entity). If in doubt, prefer 422, or maybe use the one with easier implementation.

This helps the developer to know who actually made the mistake, whether himself or the user.

By a dumb user, I mean non-technical user. An attacker shouldn't matter here: If you were that worried about, you wouldn't use HTTP status codes in the first place. :)

For example, if you're validating a JSON request body against a JSON Schema, 400 is the proper choice (e.g. missing keys, keyword mismatches). However, if you're expecting user to enter a valid email, then you should use 422 (let me say again, suppose there is no client validation).

This can also be helpful in better branching logics. For instance, in the context of a REST API, suppose you have a survey response endpoint, with possible methods of POST and PATCH. In this case, if a client sends an invalid POST request, you might want to send the URI of the new resource back in the Location header for when the status code is 422, but not for 400.

Real world examples are more complicated for sure, but this should work great IMHO.

Related