114

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?

Thunderforge
  • 2,708
  • 3
  • 24
  • 30

5 Answers5

107

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.

Kyle McVay
  • 1,937
55

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.

Fran
  • 859
  • 5
    Ive heard it described as "400 means bad syntax, 422 means bad semantics." – cbojar Aug 24 '16 at 20:44
  • 44
    All the x00 codes are "catch all" generic codes. A 400 is not inappropriate, just less specific. – Jack Aug 24 '16 at 21:11
  • 1
    Good answer, but the ansewer given by @GoFree makes more sense for me. – Ewerton Aug 30 '17 at 14:30
  • 2
    There is a really strong case for returning codes that everyone knows. Absolutely everyone will need to look up 422, and I'll bet that most still won't know what to make of it. – sylvanaar Feb 26 '18 at 17:11
20

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

<p><strong>POST</strong> an entity describing or containing the result of the action;</p>

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.

GoFree
  • 317
  • 2
    Yes, returning 404 in that case is fine. I'm not saying that server should always send 200 OK with an explanation in the body. – GoFree Mar 07 '17 at 23:27
  • Better explanation i seen so far. I have this same question and seems that all the word thinks differente from me and you @GoFree – Ewerton Aug 30 '17 at 14:27
  • @GoFree This is a really good answer to rethink API Design. But isn't it the case, that we use HTTP and its response code as eminent part of the business rules layer when we use a REST API? – Thomas Lauria Feb 14 '19 at 07:27
  • 3
    @Thomas Lauria I haven't seen a proper implementation of RESTFUL API yet. However, REST API is a buzzword today so every company has to use it (the word, not the technology) without actually understanding the concept behind it.

    So yes, it's being used in a wrong way everywhere. What programmers usually create is not a REST API, but HTTP API, or API OVER HTTP. Actually one of the few good RESTFUL implementations is the HTTP itself :)

    – GoFree Feb 15 '19 at 10:56
  • 1
    If a server responds a 2XX, I expect the success case. Regardless of the type of error, request or server-side, I never expect the 2XX range but rather 4XX or 5XX. I have worked with api returning 200 OK for error cases, and it is a pain to parse them then. – k0pernikus Jun 11 '19 at 12:51
  • 3
    Yes, you are correct to expect that the request succeeded. But on a Transportation Layer! Because HTTP status code is part of transportation protocol (as the acronym HTTP implies). Not on a Business Layer.

    A Post Office will not tell you if your tax return form inside the envelope is valid. It will only tell you if the address on the envelope is valid. Post Office deals with transportation of the envelope, just like HTTP, not with the content of the envelope.

    – GoFree Jul 02 '19 at 14:49
  • 1
    Using 200-OK is especially logical if the application includes an "error" field in the response body to describe any business-level errors such as input value too high or too low or missing, product out of stock, etc. – Roland Jul 31 '19 at 10:42
  • 5
    Just adding more weight to this, if the HTTP layer has no problem with the data then you shouldn't be sending a HTTP error to indicate an error. This causes huge problems when you're trying to debug errors, because you've used your 400 to validate your business logic, what code do you now use to validate your transport layer? – Tony Cheetham Jan 15 '20 at 15:20
  • 1
    I agree that if we follow the specification guidelines, this is the correct answer; However, in my opinion, the specification should be updated to include business logic errors. It's a real pain always having to parse the response and check if the request was successfull or not. – Jack Casas Nov 17 '20 at 10:50
  • 1
    I disagree with this answer. REST APIs do use HTTP status codes for semantics, that's the whole point of it. It lets the business layer interact with the transport layer so that it can benefit from HTTP mechanisms (caching, discoverability, etc.). 4XX statuses would have very little usefulness otherwise. Oauth uses 400 statuses to describe business errors (invalid credentials). 403 errors are business errors (some business rule denies you access to this resource). Most API frameworks throw 4XX status codes on business rule violations by default (attribute validation failures in dotnet core). – Maxime Rossini May 17 '23 at 13:46
  • 1
    Also, HTTP is not the transport layer. TCP is. Pigeons can transport HTTP requests and responses (you can write them on paper, they are text!). HTTP lives in the same layer as your business: the application layer. HTTP is just a foundation for your business rules, but in a REST API you cannot abstract from it, your business relies on most of its features. – Maxime Rossini May 17 '23 at 14:19
  • Authorisation isn’t business rules. – gnasher729 Oct 27 '23 at 08:52
  • @MaximeRossini Author of the original question is not doing REST API, but something like "RPC over HTTP", therefore rules about REST are irrelevant.

    Let's imagine you're using HTTP codes for your business rules. Let's say you receive back HTTP code 400 - Bad Request. Now you have to decide if the request is bad because your HTTP was e.g. syntactically malformed, or you broke some business rules. You're using one code to represent two completely independent situations, therefore breaking the Single Responsibility Principle.

    – GoFree Feb 11 '24 at 19:05
  • @MaximeRossini You're right, pigeons are more similar to TCP. But the idea remains, you can replace HTTP with something else. It should not affect your other application layers, e.g. business rules. – GoFree Feb 11 '24 at 19:11
  • @GoFree About the OP not doing REST, this is the first sentance of the question: "I have a REST endpoint". – Maxime Rossini Feb 13 '24 at 08:35
  • About the fact that you should be able to replace HTTP with something else: I don't see the point. HTTP is the foundation for your application layer and for all world wide web applications to use. You're gaining functionality by integrating with its mechanisms rather than abstracting from it. However, HTTP statuses should not be the only level of error codes. Your client must distinguish an "email syntax validation" error from an "email already in use" error. But all these errors can be grouped under 4XX status codes, because they rely on client input. Just use a sub error code. – Maxime Rossini Feb 13 '24 at 08:36
  • TCP is managed by your network controller and in-between network equipments, so this layer is by design abstracted away from your application code (your controller could switch to UDP seamlessly). HTTP is partially managed by your application (client and server), you CANNOT abstract it away. You can only limit the set of features that you use (avoid non-200 status codes, non-POST verbs, optional headers...) in case there's an apocalypse, but then you only use HTTP because your browser forces you to do so, and lose 90% of its features (which you then need to reimplement). – Maxime Rossini Feb 13 '24 at 10:33
  • @MaximeRossini Yes, the first sentence says that. But the fact, that the url has a verb in it means, that the endpoint is not a REST but "api over HTTP"/"RPC over HTTP". Many times people say one thing, but the reality is different. REST is an extremely specific technique how to do something and vast majority of people claiming they have REST do not actually have it. Unfortunatelly :-( – GoFree Mar 11 '24 at 14:50
  • @MaximeRossini Response of 400 Bad Request can mean e.g. that your Content-Type header is wrong. I.e. the HTTP "envelope" itself is bad. Then if you use the same 400 Bad Request to mean that e.g. email address sent in the body of the request is invalid, you're using one set of error codes to mean two different things. The recipient of the 400 Bad Request now has to understand and operate on two separate layers: 1. HTTP, 2. Your business layer. That's just bad design. – GoFree Mar 11 '24 at 14:58
  • @GoFree I agree with your 1st message. I disagree with the rest. If you don't couple your business layer with HTTP, you can never decide what HTTP method to use for your endpoint (e.g. GET for reads). In a web application, HTTP is part of your business layer. Request methods and cache headers are part of the features that HTTP exposes to your business layer, and so do status codes. Having sayed that, I don't think I added anything new to my previous messages, so we will have to agree to disagree on that I think :) – Maxime Rossini Mar 12 '24 at 16:31
  • @MaximeRossini Well, HTTP is just one way of transfering data from one system to another. But there are many other ways to transfer those data. You could e.g. connect directly to remote database to transfer those data. Or you could be connecting over ssh directly to remote filesystem. The class that uses HTTP/db connection/ssh connection needs to know how to convert business object into HTTP/db/ssh request and how to process the response. But the business layer should not even know something like that is part of the system. – GoFree Mar 13 '24 at 17:36
10

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.

Thunderforge
  • 2,708
  • 3
  • 24
  • 30
Telastyn
  • 109,398
  • “Forbidden” = nobody is ever allowed access. “Unauthorised” = you will be allowed access with the right authorisation. And both have precedence to “not found”. “Unauthorised” gives no information that the resource exists. Status 400 “bad request” - any server anywhere should return the same status. Doesn’t give you any information about the server state. The server has no idea what you want, so it cannot even return 401 or 403. – gnasher729 Oct 27 '23 at 08:51
-2

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.

dlb
  • 225
  • 6
    409 means "the state you're trying to put this in is in conflict with the state another client is trying to put this in", it's for blocking concurrent writes (using ETags, for example) – Jack Aug 24 '16 at 21:12