REST design - An operation with multiple possible non-success payloads - rest

I'm trying to design a REST method for an 'Add person' operation that has a bunch of business rules. There are multiple possible non-success payloads (for the business purposes), requiring defined structure (to allow the consumer to parse the detail).
For 'Add a person', one of the following non-successes could happen:
We believe the system already has person.
Payload: The ID of that person
There are some possible matches.
Payload: A list of possible duplicates, and an override code to submit the record 'for sure'
General validation errors
Payload: Array of 'Error' object. (Standard across the API)
Question - Response object
If they're all to return under a single HTTP error status code, would it be right to have a varied object like:
OverrideCode (for (1))
PersonPossibleMatches [] (also for (1))
PersonDuplicateId (for (2))
ErrorList [] (for (3))
And have the consumer + documentation explain the interpretation?
Question - Response code
Is 400 (Bad Request) the correct (or correct enough) HTTP status code for this? We use it largely for the field validation (also scenario (3) - just wondering if business rule / 'intermediate state' things like this are any different.
Are there a more appropriate codes to spread the 3x scenarios over? And is it ok for the payloads to be different?
Thanks.

There are two aspects you need to consider
HTTP response code.
Error response payload.
Point number 1 is relatively simple. You have 400 error code for bad requests. And 409 for conflicting resources. So far simple.
Now let us consider your scenarios:
We believe the system already has person.
Payload: The ID of that person
Design suggestion: you can send a response like below
Response code: 409
{
"error_code": "resource_exists",
"error_description": "Resource person with ID XXX already exists"
"debug_info": "",
"link" : [
{
"href": "http://host-name/persons/123456",
"rel": "person"
}
]
}
2. There are some possible matches.
Payload: A list of possible duplicates, and an override code to submit the record 'for sure'
Design suggestion:
In this case - you may want to use PUT to override the resource. No need to use special code.
Response Code: 400
{
"error_code": "potential_duplicates",
"error_description": "Potentially the resource is duplicate of one of the following. Please use PUT with the resource ID to update"
"debug_info": "",
"link" : [
{
"href": "http://host-name/persons/234",
"rel": "person"
},
{
"href": "http://host-name/persons/456",
"rel": "person"
},
{
"href": "http://host-name/persons/789",
"rel": "person"
}
]
}
General validation errors
Payload: Array of 'Error' object. (Standard across the API)
Design suggestion: Here you can simply use 400 response code and a meaningful response like the examples above.

This depends in part on how the operation is performed. Since you said the operation has a bunch of business rules, and the system returns a payload with an ID when the person already exists, let's assume the operation is non-idempotent due to unrelated side-effects, performed with a POST to a factory endpoint.
1. We believe the system already has person.
This is a no-brainer. As suggested by others, you should use a 409 Conflict status code, with a body describing the nature of the conflict. In this case, it seems like there's nothing else the user needs to do, and he can move forward to the next step in the workflow. If there's something he can do, it should follow a procedure similar to the next case.
2. There are some possible matches.
Assuming that the clients don't have any key to unambiguously identify a person, which seems to be your case since you're considering possible matches, here you should also use a 409 Conflict status code, with a body describing the nature of the conflict, but with instructions on how to solve it.
Some other answer suggests you to allow an overwrite parameter that could be used any time, other suggests using a PUT, but I disagree with that since there's nothing preventing a client from using the overwrite all the time, or skipping the POST and use the PUT to replace an existent close-match. Also, you may have concurrent clients trying to add or change a person that match each other, or a common existent group, which will lead to an ABA conflict.
The conflict resolution body should return a valid tag for each possible match, and the client should be instructed to resubmit the same request with the If-Match header and the collection of tags. It may be a single tag, as long as it's generated from key data from each member in the collection. This will enforce that the user first must try the request without any override. If there's a conflict the user is forced to specify the exact entities that will be overwritten, and you're protected from inconsistent updates in case someone changes the current state between the first and the second request.
If the tags don't match in the second request, meaning the state was changed by something else between them, you should fail with a 412 Precondition Failed error.
3. General validation errors
This is also a no-brainer. A 400 Bad Request detailing the error, which seems to be standard across your API.

You could use 409 for the duplicate entry - and arguably for the possible duplicate entries with the extra info in the payload.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10
A 400 for validation errors would be the expected response.
It is a judgement call at the end of the day and it depends what would be easier for your rest clients and what they are doing.

Here's the design process I use when creating RESTful API responses for error cases:
Design the response payload for the error condition. Regardless of the error code that's used, it is always good practice to return some content within error responses so that clients can learn more about the error and how to avoid it in the future.
If there is an HTTP status code that accurately describes this error already, and it's not already in use for another error case, use that.
If the closest matching error code is already being used for another error case, it's still OK to use that code, but the response payload becomes the place where the different error cases under that code get distinguished from each other. Your documentation should clearly state that inspecting this code wouldn't be enough, and that clients should then also look into the response to see exactly what happened.
If none of the above are applicable, use the closest error code that's appropriate. Just as in #3, the documentation of your response payloads makes this approach possible. If it's an error that the client influenced, make it a 400-range error, probably 400 - Bad Request. If it's the server's fault, then it should be a 500-range error, probably 500 - Internal Server Error.
Please, please, never throw 200 - OK for errors. The world left that nonsense behind in SOAP land, and nobody wants to go back.
Now let's apply that thinking to your error cases:
We believe the system already has that person. As correctly stated in another answer, 409 - Conflict accurately describes that error, so you should just use that. Putting some descriptive error information in the response payload would help new users of your API, even with such a definitive and understandable code.
There are some possible matches. There really isn't an HTTP code that describes this, and it's something that the client could influence, so the closest would be the catch-all code of 400 - Bad Request. Including the list of possible duplicates is an interesting idea, but make sure you don't end up returning enormous responses with huge numbers of possible matches. Also, make sure to also return URIs to those matching resources so that your clients can easily consume them. As for the "override code" suggestion, I wouldn't return that in the payload. Rather, I'd just document a parameter to your "Add a Person" operation that would allow for overrides to occur at any time, not just after a failed first attempt. For example: POST /people?overwrite=true.
General validation errors This is definitely a job for 400 - Bad Request, along with a descriptive error payload. It sounds like you're already allowing an array of errors to be returned from any API call, so that should be good enough to capture all the validation failures for the client-supplied data.

How about explaining it with a payload back; That is how we deal with REST responses for clients.
Response HTTP 409 with following payload response indicating to client what should they do next
`
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<request-result>
<http-code>200</http-code>
<description>REST Request is successfully processed</description>
<internal-error-info>Person already Exists</internal-error-info>
<message>Person with <id> already exists in sytem. Try picking different ID/Name combination</message>
<requested-operation>Add a Person</requested-operation>
<resource-name>Person</resource-name>
<status>SUCCESSFUL</status>
</request-result>
`

Related

REST API design issue

I am calling a REST API for a list of resources and getting the json response as below if atleast one resource is there.
{
"value": [
"res1_id",
"res2_id",
"res3_id"
]
}
and the HTTP response code is 200.
But when no resource is there the server is returning HTTP response code as 404.
My doubt it why it is designed that way(it is an enterprise product).
I was expecting a empty list as below and HTTP response code 200:
{
"value": []
}
I am not able to understand what design decision has been taken into consideration to return 404 instead of an empty json.
Please help me to understand the reasoning behind it.
What I am reading here is that you have a 'collection' resource. This collection resource contains a list of sub-resources.
There's two possible interpretations for this case.
The collection resource is 0-length.
The collection resource doesn't exist.
Most API's will treat a 0-length collection as a resource that still exists, with a 200 OK. An empty coffee cup is still a coffee cup. The coffee cup itself did not disappear after the last drop is gone.
There are cases where it might be desirable for a collection resource to 404. For example, if you want to indicate that a collection never existed or never has been created.
One way to think about this is the difference between an empty directory or a directory not existing. Both cases can 'tell' the developer something else.
You're trying to access a resource which does not exist. That's the reason for 404.
As a simple google search says
The HTTP 404, 404 Not Found, and 404 error message is a Hypertext
Transfer Protocol (HTTP) standard response code, in computer network
communications, to indicate that the client was able to communicate
with a given server, but the server could not find what was requested.
For your case
{
"value": []
}
This can be a status code 200 which means the resource is accessed and data is returned which is an empty array or you can customize it to a 404. But for that you've mentioned best is to return 200.
A status code of 404 is the correct response for when there is no resource to return based on a request you made. You asked for data and there is no data. It returns an empty response. They're relying on you to base your interpretation of the results on the status code.
https://en.wikipedia.org/wiki/HTTP_404
Some companies prefer making their errors known instead of hiding them. I know where I work we shy away from try catch blocks because we want our applications to blow up during testing so we can fix the problem. Not hide it. So it's possible that the dev that created the API wants you to use the status code of the response as a way of telling if the service was successful.

What is the best HTTP status code error for failed password update?

I'm building some REST API to be used in a native iOS/Android app.
One of the endpoints allows the user to update his password by providing 2 fields: old_password and password.
Which HTTP status code should I in the situation where the old_password is incorrect?
My first thought was a 401 error but I already use it when the authentication token is invalid and it automatically triggers a logout in the app.
400 doesn't seem to fit because the request is actually semantically correct, it is a specific authentication error. Maybe 422?
One thing to keep in mind is that status codes, like response headers, are metadata; they are there so that generic components that know nothing about the details of your API can participate intelligently -- for example, by invalidating caches, or throwing up dialogs to collect authentication credentials.
400 doesn't seem to fit because the request is actually semantically correct
In practice, 400 is usually fine; clients are supposed to treat an unrecognized 4xx class status code as they would treat 400. Put another way, you can use 400 unless you specifically want to induce a different behavior by the generic components.
For your specific case, 409 Conflict is probably the closest match
The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource. This code is used in situations where the user might be able to resolve the conflict and resubmit the request. The server SHOULD generate a payload that includes enough information for a user to recognize the source of the conflict.
RFC 5789 suggests a distinction between 409 and 422 that may be interesting. Paraphrasing this distinction
422: This might include attempts to modify a resource in a way that would cause the resource to become invalid
409: the request cannot be applied given the state of the resource.
You can also make a reasonable argument that the entire 4xx class of response codes is inappropriate. For example, if the request method is POST, then the server is expected to
process the representation enclosed in the request according to the resource's own specific semantics
Which it did; successfully, even. It just didn't produce the most commonly expected successful result.
On the other hand, the JSON Patch would argue the other direction; your attempt to change the password would probably look something like
[
{ "op": "test", "path": "/password", "value": "old_password" },
{ "op": "replace", "path": "/password", "value": "new_password" }
]
If you provided the wrong password in the test, then that operation would be considered unsuccessful, which would in turn mean that the PATCH is unsuccessful. That in turn would invoke the arguments for error handling as described in HTTP Patch.

RESTful name for the method that does two operations

In the API I have tickets resource and there are 2 methods:
1) Get the ticket status (ticket can be locked/unlocked)
GET /api/tickets/:id
2) Update the ticket status
PUT /api/tickets/:id with status=locked|unlocked
What I need - is to combine the status check with the actual update in one API call. And I also need to know if the status was changed with the API call or it was already set to needed value. E.g:
ticket_status = "unlocked"
PUT /api/tickets/:id/check_lock
changes ticket_status to "locked" and responses with "success"
ticket_status = "locked"
PUT /api/tickets/:id/check_lock
ticket_status is already "locked" so we response with "ignored"
For reverse operation we can have
PUT /api/tickets/:id/check_unlock
I understand that we are breaking the single responsibility principle here by combining to operations but this is done to decrease number of API requests and this is going to be called very frequently.
So I'm looking for good names instead of those ugly "check_lock", "check_unlock" as they seem to be rather ambiguous.
Using something like
PUT /api/tickets/:id/check_lock
to update the lock status is not recommended (as not to say bad practice).
When reading check_something one usually thinks of a method that is safe i.e. one does not expect it to modify any value.
Your example PUT is not idempotent. i.e. invoking PUT two times on the same resource results in different outcomes. Which contradicts the HTTP Specification for PUT
PUT request is defined as replacing the state of the target
resource.
In your case it's best to use PATCH to update part ticket resource as described in RFC5789 or even https://www.rfc-editor.org/rfc/rfc6902
PATCH /api/tickets/:id/ HTTP/1.1
Host: example.org
Content-Type: application/json-patch+json
{ "op": "replace", "path": "/lock", "value": "unlock" }
You may choose to an ETag in your response to signal whether the resource has been modified or not.
Or just the HTTP Status Code 200 with a message body in which you return whether the resource was modified or not and also the new value of the lock state.
If you want to toggle the lock status you should use something like which is called "action resource". In this case use POST.
POST /api/tickets/:id/toggle_lock
As result you can return the new lock status and whether it was updated or not as part of the body of the HTTP 200 Status Code.
Thinking in resources I would not have a operation check_unlock.
I would suggest: GET /api/tickets/:id with the status of the ticket in the json answer. Even if you have two http calls.
Or if the status results in an error, you can answer with an error 400 or something like that.
I would take a slightly different approach and use the 409 Conflict status code.
The request could not be completed due to a conflict with the current
state of the resource. This code is only allowed in situations where
it is expected that the user might be able to resolve the conflict and
resubmit the request.
Successful session :
PUT /api/tickets/123 { ... locked: true ... }
200 OK
Already locked session :
PUT /api/tickets/123 { ... locked: true ... }
409 Conflict
POST /api/ticket-queue { // ticket data here }
201 Created

HTTP Status codes confusion

So, I am trying to display meaningful and accurate HTTP status codes and I'm getting confused even after going through the examples online. Here's my situation:
In the URL which accepts the query param:
#POST query
Sample URL: /putRecord?Name=A&Age=25&house=permanent
what status code do i return when one of the params is missing? (All 3 are mandatory) - from what I've read, I am guessing it is a 400: Bad request. But this seems too general. Is there a way for me to say that the query was malformed? Source:here
Assuming I validate the name for duplicates against the DB, if I get a duplicate, what status code do I return?
So, if there's a duplicate entry available already, I can't add the record. Do I display 304 (Not modified) or just the error code from 2?
Clarification: For 3, another possibility is when I can't actually commit to the DB and I rollback(possibly). What status code will it be now?
Thanks so much!
Ad 1. You can return, besides the code itself (400) also a short message with explanation. So, instead of returing "400 Bad Request" you could respond with "400 Query param name required" or something similar. This message is called "reason phrase" and is defined in 6.1 in RFC 2616. Or, if you need more flexibility, you could return document with the explanation.
Ad 2. I would consider using 422 Unprocessable entity. From the RFC 4918:
The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity, 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.
Ad 3. I would return 422, but it really depends whether this situation is considered an error in your logic, or is it a regular, expected situation.
Edit: As #War10ck suggested in the comment, HTTP 409 (in place of HTTP 422) might make sense as well.
Note on handling duplicates: it seems (correct me if I'm wrong) that you consider new entity being a duplicate if it's name is already in the database. If so, that you could maybe consider using a HTTP PUT instead of HTTP POST?
You could define the following resource:
HTTP PUT /record/:name
So, "name" would be a part of the URI. Then, in case there is a second PUT to the same resource (same "name"), it'd be elegant to respond with 409/422.
If you're using different key for unique constraint, modify the URI appropriately.
As a rule of thumb, POST is for situations where you can have multiple instances of a given resource, i.e.
HTTP POST /log ;; Because we have many logs
And PUT for situations where each resource is unique:
HTTP PUT /person/:name (or /person/:tax-number if :name isn't unique)
Also, note that I've renamed your resource from "putRecord" to "record" - PUT is a HTTP method, no reason to have it in URI as well.

HTTP status code in REST API for using GET to query a “Not Ready Yet, Try Again Later” resource?

First of all, I've read some relevant posts:
Best HTTP status code in REST API for “Not Ready Yet, Try Again Later”? It is about GET an item
Is it wrong to return 202 “Accepted” in response to HTTP GET? It is about GET an item
HTTP Status Code for Resource not yet available It is about POST
HTTP status code for in progress? It is about GET but no clear answer.
but I still think I should raise my question and my thoughts here. What should be the HTTP status code in REST API for using GET to QUERY a “Not Ready Yet, Try Again Later” resource? For example, client tries to query all local news happen in future(!) by make an HTTP GET to this url: http://example.com/news?city=chicago&date=2099-12-31 so what shall the server reply?
These are the http status code I considered, their rfc definition and why I am not fully satisfied with:
3xx Redirection. Comment: Not an option because there is no other link to be redirected to.
503 Service Unavailable: The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. The implication is that this is a temporary condition which will be alleviated after some delay. If known, the length of the delay MAY be indicated in a Retry-After header. Comment: The retry behavior is desired, but semantically the situation is not server's fault at all, so all 5xx look weird.
4xx Client Error. Comment: Looks promising. See below.
413 Request Entity Too Large: The server is refusing to process a request because the request entity is larger than the server is willing or able to process. ... If the condition is temporary, the server SHOULD include a Retry- After header field to indicate that it is temporary and after what time the client MAY try again. Comment: The retry behavior is desired, however the "Entity Too Large" part is somewhat misleading.
417 Expectation Failed: The expectation given in an Expect request-header field (see section 14.20) could not be met by this server. Comment: So it should be caused by an Expect request-header, not applicable to my case.
406 Not Acceptable: The resource ... not acceptable according to the accept headers sent in the request. Comment: so it is caused by the Accept request-header, not applicable to my case.
409 Conflict: The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. ... Conflicts are most likely to occur in response to a PUT request. Comment: This one is close. Although my case is not about PUT, and isn't actually caused by conflict.
404 Not Found: The server has not found anything matching the Request-URI. Comment: Technically, my url path (http://example.com/news) DOES exist, it is the parameters causing problems. In this case, returning an empty collection instead of a 404, is probably more appropriate.
403 Forbidden: The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. Comment: Generally this is supposed to be used in any restricted resource?
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. Comment: It is not true in my case. My server understands the request, its syntax is good, only the meaning is bad.
2xx Successful. Comment: If 4xx doesn't work, how about 2xx? See below.
200 OK. Comment: Fine. So what should I include in the response body? null or [] or {} or {"date": "2099-12-31", "content_list": null} or ... which one is more intuitive? On the other hand, I prefer a way to clearly differentiate the minor "future news" error from the more common "all query criteria are good, just no news this day" situation.
202 Accepted: The request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon. Comment: Providing that we can use 202 in a GET request, it is acceptable. Then refer to the 200 comment.
204 No Content: The server has fulfilled the request but does not need to return an entity-body. Comment: Providing that we can use 204 in a GET request, it is acceptable. Just don't know whether this is better than 202 or 200.
More on 2xx: Comment: I assume all 2xx response will likely be cached somewhere. But in my case, if I return an empty body for "tomorrow's news", I don't want it to be cached. Ok, explicitly specify the "no cache" headers should help.
Your thoughts?
Use 404.
Your objection to it is based on a popular understanding of a URI as not including the querystring. "Because I have multiple URI's that map to the same handler," goes the logic, "my resource does in fact exist and is just being parameterized by querystring args."
This is incorrect. As the URI spec itself says in Section 3.3 (emphasis mine),
"The path component contains data, usually organized in hierarchical
form, that, along with data in the non-hierarchical query
component (Section 3.4), serves to identify a resource within the
scope of the URI's scheme and naming authority (if any)."
Resources are identified by URI's, and any change to any part of an absolute-URI identifies a separate resource. Tweet that to everyone you know once a day until they tell you to stop. Therefore a 404 is a perfect match: "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."
You are retrieving the news for that day, which is a valid day, there just isn't any news. A 200 response of an empty body, or a what ever makes sense based on the mediatype would seem the logical. This depends on the media type you have decided with the client.
404 would make more sense if the date format was wrong (you asked for the 45th day of November, or asked for a city that doesn't exist.)
As an aside the URL would be better in the format http://example.com/news/chicago/2099-12-31 since that is the specific resource you want to retrieve. This format would make things like 404s clearer as well.