HTTP status codes are like short messages returned from a server whenever we request or interact with a resource on the server. They are an invaluable asset for diagnosing application errors and help to fix them quickly.
Each HTTP status code has a specific and unique meaning, however, there are cases where developers might confuse the purpose of a status code with another status code with almost similar usage. In this article, we will be discussing some of the common mistakes that developers do when using HTTP status codes.
HTTP Status Codes For Invalid Data: 400 vs 422
To understand the difference between these two status codes, let’s assume a case where you pass the string value “password” for the key password in the request body but the developer has blacklisted this string value on the server side to prevent users from using this as their password. In this case, what should be returned as the response status code: 400 or 422?
If you were thinking 400, in that case, let’s think it out loud once. You have passed a string value to the API endpoint that expected a string value, but the value of the string contained data that has been blacklisted or we can say, isn’t satisfying the condition for further processing on the server. Hence, the syntax of the request isn’t wrong, so, we can’t return a 400 status code saying bad request. According to w3.org, “400 Bad Request” means:
The server was unable to understand the request due to incorrect syntax. The client SHOULD NOT REPEAT the request UNALTERED.
Therefore, we can use the status code “422 Unprocessable Entity” in this scenario. That would mean that the server understands what you are trying to do, and it understands the data that you are submitting, but it simply won’t let that data be processed further.
The 422 (Unprocessable Entity) status code indicates that the server recognized the request entity's content type but was unable to process the instructions contained within it.
The status code 422 can be returned in cases such as an error condition where the XML request body contains well-formed and syntactically correct XML but it is semantically erroneous.
Handling Forbidden RESTful Requests: 401 vs 403 vs 404
To explain this issue, say we have two users in our system: Sam with ID 4 and Rick with ID 7. Assume Sam attempts an authorized API call to see Rick's profile resource, as shown below:
GET /users/7/profile HTTP/1.1
Authorization: Basic YmVuK2F206dGVzdA==
Accept: application/json
Here, Sam is using Basic Authorization to identify himself as Sam, but he's making a call to access another person's profile, and let's suppose that a user can only read his or her own profile in this application.
So, the question here would be what status code should be returned? The most appropriate ones could be:
- Sam is not authorized to view Rick’s profile, so “401 Unauthorized”?
- Sam is forbidden from viewing someone else’s profile, so “403 Forbidden”?
- Sam can’t simply see resources that he’s not allowed to see, so “404 Not Found”?
However, if you return 401 or 403, you will be disclosing secure information since, in both circumstances, we are declaring that the resource exists but you can't view it, thereby verifying the resource's existence. So, can we use 404 in this case? Let's see what happens.
The description of “403 Forbidden” says that:
The server comprehended the request but refused to perform it. Authorization is useless, and the request SHOULD NOT BE REPEATED.
If the request method was not HEAD and the server intends to make public the reason why the request was not completed, the entity SHOULD specify the reason for the rejection.
If the server does not want the client to get this information, the status code 404 (Not Found) might be used instead.
Well, this should solve the problem. If you don’t want to expose the information, you should return a “404 Not Found” instead.
Empty resource HTTP status code: 200 vs 204 vs 404
To illustrate this, imagine there is a method GET /users
that returns a list of all users and GET /users?name=sam
provides a list of only users with the name sam. What HTTP status code should be issued if there is no user named Sam?
The first and most likely option is 200 Success, as the request would be successful and an empty list would be returned. It might be claimed that the /users
collection/list resource exists and that the name query param is used to filter the list's content.
However, the “204 No Content” status code definition says:
The 204 (No Content) status code indicates that the server has successfully fulfilled the request and that there is no additional content to send in the response payload body.
While 200 OK is a common and acceptable answer, 204 No Content may make sense if there is nothing to return. It is most typically used in response to a PUT (replace) or a PATCH (partial update) when servers do not wish to deliver the replaced/updated resource, or in reaction to a DELETE, because there is usually nothing to return after a deletion. It may, however, be used on a GET.
If the request is valid, has been correctly fulfilled, and there is no more information to send (which is the case because the returned list is empty), the answer 204 No Content is understandable and valid.
However, a 404 Not Found status code’s definition says:
The 404 (Not Found) response code indicates that the origin server was unable to locate a current representation of the requested resource or is unwilling to reveal the existence of one.
Assuming that /users
is the resource used even when doing a GET /users?name=sam
, and obtaining a 404 HTTP status code makes no sense because the resource /users
exists, it's only that the list it contains may be empty.
That concludes this article; please share any more use cases in which you encountered confusion and how you resolved it. I hope you found this article useful.
Keep reading!