Say that I have a REST endpoint that takes an integer as a parameter:
/makeWaffles?numberOfWaffles=3
In this case, I want the number to be positive because I can’t make a negative number of waffles (and requesting 0 waffles is a waste of time). So I want to reject any request that does not contain a positive integer. I also want to reject a request that exceeds some maximum integer (let’s say for now that it’s MAX_INTEGER).
In the event that someone requests a non-positive number of waffles, should I return an HTTP 400 (Bad Request) status? My initial thought is yes: it is not a valid number for me to complete the request. However, the RFC doesn’t mention business rules as a reason to throw it:
The 400 (Bad Request) status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
A business rule doesn’t fall under any of those three examples. It’s syntactically correct, it’s properly framed, and it’s not deceptive request routing.
So should I return an HTTP 400 (Bad Request) status if a parameter is syntactically correct, but violates a business rule? Or is there a more appropriate status to return?
2
This is a great question, and still highly relevant given the historical context (and seemingly contradictory definitions) of the HTTP return codes. Even among the answers to this question there are conflicting definitions. This can be clarified by moving chronologically.
RFC 2616 (June 1999)
10.4.1 400 Bad Request
The request could not be understood by the server due to malformed
syntax. The client SHOULD NOT repeat the request without
modifications.
As of this RFC, this status code specifically applied only to syntactically invalid requests. There was a gap in the status codes for semantic validation. Thus, when RFC 4918 came around, a new code was born.
RFC 4918 (June 2007)
11.2. 422 Unprocessable Entity
The 422 (Unprocessable Entity) status code means the server
understands the content type of the request entity (hence a
415(Unsupported Media Type) status code is inappropriate), and the
syntax of the request entity is correct (thus a 400 (Bad Request)
status code is inappropriate) but was unable to process the contained
instructions. For example, this error condition may occur if an XML
request body contains well-formed (i.e., syntactically correct), but
semantically erroneous, XML instructions.
422 Unprocessable Entity was created to fill the gap of semantic validation in the original specification of the 4xx status codes. However, another relevant RFC came about in 2014 which generalized 400 to no longer be specific to syntax.
RFC 7231 (June 2014, explicitly obsoletes RFC 2616)
6.5.1. 400 Bad Request
The 400 (Bad Request) status code indicates that the server cannot or
will not process the request due to something that is perceived to be
a client error (e.g., malformed request syntax, invalid request
message framing, or deceptive request routing).
Note that the 422 description says that the reason 400 is inappropriate is because 400 (as of RFC 2616) should be returned only for bad request syntax. However, as of RFC 7231, the strict syntax-error definition no longer applies to 400.
Back to the question at hand: While 422 is technically more specific, given this context, I could see either 400 or 422 being used for semantic validation of API parameters. I’m hesitant to use 422 in my own APIs because the definition of 422 is technically outdated at this point (although I don’t know if that’s officially recognized anywhere). The article referenced in Fran’s answer that encourages the use of 422 was written in 2012, two years before RFC 7231 clarified HTTP 400. Just be sure to standardize on one or the other.
I read the first answer and didn’t really agree with it because, at least in my reading, a bad request (400) means, “I can’t even handle your request because something is fundamentally wrong.” And I found this post which makes the case for returning a 422.
from IETF
422 Unprocessable Entity (WebDAV; RFC 4918)
The request was well-formed but was unable to be followed due to semantic errors
This seems like a more appropriate response since your request is well-formed, but doesn’t pass your validation rules.
4
No, you should not. HTTP codes are meant for the HTTP layer of your application. Business rules is completely different layer and is application specific, so you need to come up with your own “protocol”.
Imagine an apocalypse happens and you have to switch from HTTP protocol to using Pigeons. Pigeons don’t have any return codes, so you would need to change your business layer to accomodate for that. But your business has not really changed in any way so you shouldn’t need to change business layer. It shows a tight coupling between those two layers (transport and business).
Back to the question: What you should do is return “200 OK” with a body describing what happened with the request. The specification clearly says so:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1.
200 OK
The request has succeeded. The information returned with the response is dependent on the method used in the request, for example
POST an entity describing or containing the result of the action;
Has your HTTP request succeeded? Yes it has, the web server knows what to do with that request (e.g. pass it on to the server part of your application). Web server then passes this request to the server part of your application, which takes the POSTed data, processes them (validates them in your case) and returns a normal answer (in the body of an HTTP response) to the client part of your application. This answer from the server part of application can be either “Great, the data you sent me are valid” or “I’m sorry, the data you sent are not valid”. And it is up to your client part of application to understand what the answer means.
PS: “400 Bad Request” means, that the web server did not understand what you want from it. It means that your server part of your application did not even receive the request. Or at least that’s what it’s meant to mean 🙂
EDIT: I just realized, that you’re doing GET request, not POST. This is a bad idea because GET should not change the state of the application. GET is supposed to simply retrieve data from the server. But in your request you are actually changing the state of the application, so you should really be using POST.
9
Yes, input that doesn’t follow the implied contract of the endpoint is “something perceived to be a client error”, and should return 400.
The exceptions to this is if the business rule is security related (then 401 Unauthorized
or 403 Forbidden
would be better). Alternatively, if sending a 400 would leak information about something’s existence, and then a 404 Not Found
may be more appropriate.
0
Not sure that all would agree, but we are using 409 – Conflict. Many state the 409 is more of a conflict with system state, but we accepted the interpretation that a conflict of data values being outside of accepted range is fixable by the requester and an acceptable use of 409. 422 I think would be reasonable as the request is correctly formed but cannot be processed as requested. I opine however if you do not wish to implement a host of replies, just giving a 400 is still acceptable.
1