At that point, you rely on the body content to tell you what the service correctly determined for you. A result that the user doesn't like is way different than a result that comes about because something was done wrong at the client side (4xx) or a failure on the server side (5xx).
Personally I prefer to think of HTTP as a transport mechanism, with business logic at a higher level. The question "If I replaced this http transport with, say, FTP or a dvd drop, would it require changes at the business logic level?" feels important to me.
That preference puts me on the wrong side of history at the moment though, and Im going with history. Lets express our business logic through the error system provided by our transport mechanism instead....
But a provided credit card number not existing is not success, it is unambiguously a failure.
HTTP is a transport but its response codes are clearly designed to map somehow to the "business logic" of the thing underneath it. We have to twist ourselves up in knots to map most of our business systems to the set of status codes that make sense for the default HTTP-fronted application (Fields' thesis stuff) but that's what we sign up for when we decide to expose our business services via HTTP. Other transports require different contortions.
But the failure is ambiguous. If I get a 404 back I can't rely on that to mean the credit card doesn't exist. It could mean that I have the wrong URL or that the service application screwed up their routing code.
And who's to say you can't put the reason in the body and still keep the code? What are you hurting by sending back 400? Unless you have lb's taking out nodes because of excessive 4xx's (which sounds like insanity) I don't see a reason _not_ to send 4xx's. At the very least it's a useful heuristic tool.
I had this discussion recently about 'security' with regard to X-Header versus ?query=param. Either it's http all plaintext on the network or it's http with tls all cyphertext on the network. Every bit in the http request and response is equivalent - verb, path, headers, body, etc - agree?
You could represent the card number as cyphertext in the request body, that's a good practice regardless of tls, but of course don't roll your own crypto. You could put that cyphertext in the path as well but if the cyphertext isn't stable that makes for a huge mess of paths.
You could make a case for trad 'combined' access logs situation with the path disclosed in log files. I can appreciate keeping uris 'clean' makes it safe to integrate a world of http monitoring tools, I would make this argument. In the case of the card represented in a stable cyphertext it's kinda cool to expose it safely to those tools.
Anything else?
Often systems will have application wide error handling to catch that and handle it in a systemic way. It can be a pain to short circuit that in a customizable off the shelf applications like Salesforce.
Philosophically, 4XX means the client did something wrong. Sending invalid data to a validation service is not doing something wrong.
The precise definition of 400 Bad Request is
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).
There's some room for interpretation for the phrase "process the request". If the only job of the service is to validate the request and return Valid or Invalid, then I guess yeah even an invalid request will have been processed, and 400 Bad Request may not be totally right. I think you could make a case for it! But I think you could also make a case for 200 OK with a more detailed error message and code in the body.But if the service both validates the request and _then_ does something more substantial with it, I think 400 Bad Request is probably the most appropriate response to something that fails at step one.
The payoff of using 400 is you can watch your 400 rates with almost no effort (HTTP is well established and there's many many tools out there.) If you somehow start accidentally munging the card number sometimes or if your card processor starts doing wacky stuff you'll see a spike in 400 rates.
If it was really that troubling that declined cards are expected, I would personally at least want to see 200 come from the internal API and 400 go out to the client.
And if your "intermediate devices" start doing goofy stuff to 400's then you've got bigger problems... 4xx's shouldn't be taking nodes out of prod. That's wack.
Welcome to AWS, where this is actually part of standard procedure (at least Elastic Beanstalk does this, not sure if it’s actually Elastic Loadbalancer under the hood).
400 is "bad request"; you might use it if the request body was not valid JSON.
404 means "this API doesn't exist", not "the API exists but it returns an error"
The 404 (Not Found) status code indicates that the origin
server did not find a current representation for the target
resource or is not willing to disclose that one exists.
Resource here is a transport-level (HTTP) representation of a business-logic concept, in most cases a document, but if your HTTP server happens to front a credit card validation service, potentially a credit card. Or a user. Or a recipe.And let me say it's totally valid to just ignore that advice, and say that here, in this company, we use 200 OK for everything that isn't a parse failure. And that's not totally wrong! It won't break anything. You're not really leveraging any of the power of your transport but maybe that's fine for your situation.
It can get a little fuzzy for 401 and 403 errors, though; those seem to be returned by APIs pretty often. Not entirely sure how I feel about that, but I think those are a bit more sensible.
> The first digit of the Status-Code defines the class of response. The last two digits do not have any categorization role. There are 5 values for the first digit:
> - 4xx: Client Error - The request contains bad syntax or cannot be fulfilled
What do you think status codes like 404, 405, 406 are for? You say they shouldn't be for "did the application successfully validate the user's input data" but status code 400 is explicitly for bad requests. In your view should a HTTP server ever return 4xx?
Obviously if you send a TAIL method request, you should get a 405, and if you send Accept: eggs/*, you should get a 406. If a route doesn't exist, you should get a 404. If you fail HTTP basic auth, you should get a 403 (but why are you using HTTP basic auth?). If you want certain paths to never be accessed for some reason, you should return a 401.
Doesn't this depend on the nature of the REST API? (Though putting a credit card number on the path doesn't seem like a good practice.)
You seem to be using a strange definition of "application", but assuming you mean "transport" — I mean, this is obviously not true, right?
If you sent a perfectly semantically valid HTTP request for a resource that doesn't exist on the remote, that's a 404. But by your logic, you're saying it should be a 200, because there wasn't a failure in (essentially) nginx or the HTTP library of your application server?
HTTP is a transport designed for a specific type of application (document-oriented resources) and its status codes (and lots of other things) reflect that initial coupling, so it's always gonna be a pain when you swap out the application (to a credit card processing service or whatever) and have to think about how the status code mapping will work. And you can certainly "cheat" and say that every request that doesn't crash the server is 200 OK, I guess, but that's pretty clearly not what the Hyper Text Transfer Protocol wants you to do. Its set of status codes clearly reflect details of the underlying application.
>If you sent a perfectly semantically valid HTTP request for a resource that doesn't exist on the remote
Requesting a path (via the application layer) that doesn't exist is a client error, hence the 4XX. But, say, if your website has a search bar and a client searches for "blahblah-thisdoesntexist" and gets no results, the webpage should return a 200 (the request was valid: the client sent form data to an existing resource and it was processed successfully) with something like "no results found" in the body -- because the search function itself is above the application layer. This is what I mean about "is the CC is authorized or not" being above the application layer.
>Its set of status codes clearly reflect details of the underlying application.
This is where I think you're wrong: HTTP status codes reflect the result of the HTTP request itself, not the next application in the chain, if one exists.
This can also be expressed if we look lower down the chain. If my HTTP request gave me a 404, should an error be shown in the TCP stack? Well no, because TCP doesn't know or care about HTTP, as far as it's concerned the TCP connection is established and happy. Likewise, in this example, the API HTTP stack doesn't care about whether the data was a CC or something else or what the result was: as far as the HTTP stack is concerned, some JSON got passed in and parsed properly, and some output got sent back, and everything went well so "200".
I understand OSI, but it's not a useful model in this circumstance, because it draws no distinction between the HTTP layer and the business logic served over HTTP. The complexities of that interface is what we're talking about.
> HTTP status codes reflect the result of the HTTP request itself, not the next application in the chain, if one exists.
Let's say I request /robotz.txt and I get 404 Not Found.
How do you maintain that 404 is a result of "the HTTP request itself" and not the HTTP server handing the request to the component that looks on the filesystem (or in the cache, or in MongoDB, or...) and not finding the resource?
>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).
Of course, a lot of things could be said to be a client error.
Specifically, you want to be able to easily distinguish between "your url is wrong" or "your authentication credentials are wrong" or "the API endpoint threw an exception" type errors and "credit card processing failed" errors.
It's easier long run to put business logic errors somewhere separate from protocol/routing layer (i.e. even a http header would be better) so that you can tell what is Rails/Flask/whatever failing vs. logic failing. This also gives you more flexibility to do stuff at the hardware layer (another commenter mentioned ELB) without interfering with the application layer.
Generally, 4xx errors means 'Client screwed up'. There's probably no point sending this message again, it isn't going to work until something is fixed. That might mean it will work if, for example, the account is funded or card unlocked. But something needs to be done on behalf of the client.
5xx errors mean 'Server screwed up'. It probably _is_ worth having another attempt at sending the request. Maybe it was a temporary glitch, or maybe a new release of the server has to happen. Regardless, there was probably nothing inherently wrong with the actual request.
You could argue either way. What's important is being consistent, at the very least throughout an API but preferably throughout the whole organization.
(Personally I'd probably lean towards a 40x of some kind, just make sure it doesn't clash with something that you care about.)
Along the same lines, and arguably more important, is how to log the operations where a transaction completed successfully but with a negative answer. If you log expected negatives as errors you can get error blindness.
> A "HTTP 400 bad request" is only the sort of thing you get to ignore when it's coming from the great unwashed masses who are lobbing random crazy crap at you, trying to break you. Internally, that's something entirely different.
If this is an internal-only API then most people look at 400s as their bug. Sounds like you are in the same boat.
If it's an external API, it's not their bug.
But at the end of the day, your app must target a subset of available APIs. If you use every available API the surface will be too big and you will have snowballing complexity. So if you are already using another API which can signal what you’re trying to signal then feel free to institute a “everything we can expect returns 200” policy. That kind of decision is critical for putting an upper bound on your system complexity.
I want to institute a “no default exports” policy in the JavaScript app I’m working on. It’s not that there aren’t places a default export makes sense—there are. It’s that prohibiting them will decrease the number of things our coders have to think about.
This is a line I hear a lot in regards to this general situation, and the interesting part is that the 400 code really does not signal the thing the developer is trying to signal, not at all. But a lot of devs have a weird hang-up about this for some reason, and will go to any lengths to not send a 200 response. I blame the REST cultists.
HTTP status codes can be used to good effect if done thoughtfully, because there's often already a lot of logic in the useragent to handle some cases (such as redirects, etc). Not using anything except for 200 is limiting. Using a bunch of status codes to refer to things that make no sense is confusing. Defining a sane standard for how your application will choose between them that allows for the benefits they offer without confusing clients as to what is actually being communicated is the best of both though.
I just had a discussion about this yesterday where we have an internal JSON API that auths a credit card, and if the card is declined it returns a status and a message. Another developer wanted it to return a 4xx error, but that made me uneasy. I think you could make a good argument either way, but to me that isn't a failure you'd present at the HTTP layer. 4xx is better than 5xx, but I was still worried how intermediate devices would interfere. (E.g. an AWS ELB will take your node out of service if it gives too many 5xxs, and IIS can do some crazy things if your app returns a 401.) Also I don't want declined cards to show up in system-level monitoring. But what do other folks think? I believe smart people can make a case either way.
EDIT: Btw based on these Stack Overflow answers I'm in the minority: https://stackoverflow.com/questions/9381520/what-is-the-appr...