I've found that Http 207 can be used for bulk insert/delete/... etc. operations where we potentially have multiple items being inserted and partial success is ok from business perspective.
I wonder if the same thing applies if we have API Post calling some external services?
Example business scenario:
We have HttpPost endpoint that is creating an order.
HttpPost:
/api/order/
Now let's assume that this endpoint besides creation of an Order would be calling external service to also print a label for that order and another one to send some kind of confirmation via REST to external 3rd party system.
Now in this scenario we assume that creation of Order is a must for the success of this operation but both printing and external REST call can return error message. Potentially that is besides our control.
Now from what I've read the options on how to return this information would be:
Http 200 with some kind of object containing necessary information. For example:
{
"isSucess": true,
"isFailure": true,
"erorMesage": "string"
}
Make the endpoint more RESTful and separate it into 3 actions which would be called, but in this case this is not applicable (at least from my perspective) because all of those things are tightly coupled from business perspective and they should be bound together
Return 5xx error. 502 since not all of the actions succeeded and there were some internal problems, but what if one failed action is 502 and another 503
Return 207 error with proper error response for each of the actions (Order creation: 200, Printing: 502, External 3rd party: 502)
Now I wonder which would be most suitable ? I'm kind of leaning towards 502 error or 207 since they in my opinion provide the most information about the actual state of the system.
This is a design question for how to use REST web service for a nested distributed transaction. There are many suitable answers.
Here is my take on this, where I address each of your points, while referring to the REST spec.
HTTP code 207 is not a good idea - This return code (207) is specified for systems that support WebDAV (https://en.wikipedia.org/wiki/WebDAV) extension on HTTP. WebDAV is distributed, however it is a specification that
allows user agents to collaboratively author contents directly in an
HTTP web server
WebDAV is not exactly built for distributed transactions. Servers that can handle return code 207 need to have the WebDAV module installed in them. You may not even get support in several REST libraries for this return code. For example this API docs does not have code 207. https://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Response.Status.html (this is an edge case, but it helps make the point).
I prefer not use HTTP 50x if the request is partially fulfilled. Lets take the case when the server has created the order but could not post it to the third party REST server (either that server was down or network error). Here is the spec for 50x errors https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html:
"Response status codes beginning with the digit "5" indicate cases in
which the server is aware that it has erred or is incapable of
performing the request. "
The server is not at fault here. It has created the entity.
My suggestion is to return HTTP cod 202 on POST /api/order/, return a reference to the order and process the steps asynchronously. There are multiple transactions involved in this request - printing label, updating third party REST endpoint, etc. These can be updated in the "order" resource.
For example, the POST /api/order/ can return in this format:
HTTP STATUS 202 (Accepted)
{
"order": {
"id": "1234567"
}
}
The client should check for the complete status with GET /api/order/, which will return the individual status:
{ "id": 1234567, "label_printed": "failed", "third_party_updated":
"success"}
If any of the sub-transactions of an order has failed and should be reprocessed, it is an update on the existing order, and a call to PUT /api/order/, because the PUT method is to update the resource. The server will take the PUT request and retry the transactions as needed.
Here are some additional answers that are suitable for this question:
Transactions in REST?
Transactions across REST microservices?
Related
A microservice I develop exposes a single endpoint, PUT /deployments/{uuid} . The endpoint is used to initiate a potentially expensive deployment operation, so we only ever want it to happen once, which is why we chose PUT + UUID over POST (for uniqueness). The deployment is immutable, so it can never be updated, so we currently raise an exception if the PUT is called more than once with the same uuid.
As a person who loves bikeshedding and therefore cares deeply about restfulness, this grinds my gears. PUT is supposed to be idempotent, so raising an exception after making the same request multiple times is an antipattern. However, we have a requirement to not allow sequential identical requests to generate new deployments, so the usual POST is out.
While the best solution is one that works, I'd like ours to be a little more elegant, if possible. I've posited a POST with the UUID in the payload, but my team seems to think that's worse than the current solution. I'm considering just returning a 200 OK from a PUT to the same UUID rather than a 201 CREATED, but I'm not sure if that has the same problem as non-idempotent-put in not semantically conveying the information I want.
Is there a "best solution" here? Or am I doomed to be "that guy" on my team if I pursue this further (joke's on you i'm already that guy).
tl;dr What is the correct RESTful API signature for a /deployments endpoint that is immutable, and required to not allow the same request to be processed twice?
Idempotent does not mean "2 identical requests should yield the same response". It means: "The server state after 2 identical requests should be the same as when only 1 is made".
A similar example, if you call DELETE on a resource and get a 204 No Content back, and call DELETE again and get a 404, this doesn't violate the idempotency requirement. After the second delete the resource is still removed, just like it was after the first.
So multiple identical idempotent requests are allowed to give different responses.
That said though, I think it might be nicer to the second identical request to also get a 2xx status back. It doesn't have to be the same as the first.
The use-case is if a client sent a HTTP request but got disconnected before it got a response. The client should retry and if the server detects the request is the same as the first, the server can just give the client a success response (but don't do anything).
This is generally a good idea, because if the client got an error back for the second request, it might be harder to know if the request failed because it succeeded earlier, or for other reasons.
That all being said though, there is also a way here to have your cake and eat it too.
A client could send the following header along with the PUT request:
If-None-Match: *
If the client omits the header, you can always return 424 Precondition Required.
If the resource does not yet exist, it's a success response. If the resource was created earlier, you can return 412 Precondition Failed.
Using this mechanism a client has a standard way to figure out that the request failed because a successful one was made earlier.
Based on the docs here, PUT is the best method to use. The response should be 201 when it triggers the deployment, and either 200 or 204 when nothing is changed. It shouldn't be POST because calling a POST endpoint twice should trigger the effect both times.
I am designing a REST API for registering enrollments to classes. In my of my endpoints, I can POST an enrollment:
POST to http://my-api/class/learn-rest/enrollment
This creates a new enrollment. However, in this case, there can only be a fixed number of enrollments, let's say 5.
Which HTTP response code should I return when the user tries to add the 6th enrollment?
Good question. Not sure why it's down-voted.
I'd suggest 400. It's the one you should use if you can't find a specific and appropriate status code for your 4xx errors.
409: it's inappropriate because it's usually retriable. But certainly retrying in your case would resolve the problem.
429: it's also retriable.
Did more research (some practices used by well-known api providers)
LimitExceededException: Returned if the request results in one of the following limits being exceeded, a vault limit, a tags limit, or the provisioned capacity limit. 400 Bad Request
https://docs.aws.amazon.com/amazonglacier/latest/dev/api-error-responses.html
Unless a more specific error status is appropriate for the given request, services SHOULD return "400 Bad Request" and an error payload conforming to the error response guidance provided in the Microsoft REST API Guidelines.
https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md#1521-error-response
While suggesting some specific HTTP code may be an opinion based answer, there is one things that you should keep in mind - this is should be a 4xx Client error:
4xx Client errors: This class of status code is intended for situations in which the error seems to have been caused by the client.
Among existing errors, the following looks like the most suitable for you:
409 Conflict: Indicates that the request could not be processed because of conflict in the request, such as an edit conflict between multiple simultaneous updates.
I think so cause there is a next possible scenario: let's say you set 5 as the limit of enrollments, 4 already exist in system and server receives 2 requests at the same time to create a new enrollment. In this case, only one of the requests (the first one for server) is OK.
I apologise if there is an answer for this already. I found related questions (e.g. HTTP status code for a partial successful request) but not exactly the same.
I'm building an API that returns data that is aggregated from multiple sources. Sometimes, some critical part of the data is unavailable and the client has to be made aware and error data is included in the response.
Other than the severity of the missing field(s), the rest of the resource is valid and useful and could be regarded as a partial update (e.g. if you only had permissions to see part of a resource).
So far, the options are
return 200, consider it a partial resource, handle the error data fields in the application as you would with any other data
return 207 to emphasise it's not fully successfully, but 207 is not strictly HTTP.
return 500 and handle the successfully returned data in the application as you would on a 200
I'm partial to option 1 but I'm not fully convinced. Is there a clear way of handling this? Perhaps defining a specific content-type?
You are missing the point here because a 500 is indicative of a failure of the system or the chain of communication, and since data is returned then it must be assumed that the resource exists and is found. What the OP has indicated is a partial result, implying composite data pertaining to the resource. This is necessarilly outside of the scope of http, which has done its job via a successful 200, unless you have elected a contract under which partial data is erroneous and thus a 40x.
I have REST service that manage the resource EASYPAY.. In this moment this service exposes 3 different methods:
Get a EasyPay request (GET);
Insert a Easypay request (POST);
Update a Easypay request (PUT).
Whe I inserto or update a request I must insert also a row on a trace table on my database.
Now I have to delete a Easypay request and I must add also a row on the trace table.
I wanted to use the DELETE HTTP verb, but I saw that with delete I cannot pass a complex object but just the ID of the request to delete.
I cannot use the PUT HTTP verb because I have already used it, and in any case it would not be conceptually correct...
I do not want to do more that one call from client to server (one for delete the request, the other one to add a row in the trace table)..
So I do not know how to solve the problem.
EDIT
I try to explain better... I have a web site that is deployed on two different server. One for front-end and one for Back-end. Back-end expose some REST services just for front-end and it has no access to internet (just to intranet).
The customer that access the web site can do a payment via a system called XPAY and it works really similar to paypal (XPAY is just another virtual POS).
So when the customer try to do a payment, I save some information on the database + I trace the attempt of the payment, then he is redirected to XPAY. There, he can do the payment. At the endy XPAY return to the web-site (the front end) communicating us the result of the payment.
The result is in the URL of payment, so i must take all the information in the url and send them to the back-end.
According to the result, I must update (if result is ok) or delete (if result is ko) the information I saved before and write a row on the trace table.
What do you suggest?
Thank you
There are actually a couple of ways to solve your problem. First, REST is just an architectural style and not a protocol. Therefore REST does not dictate how an URI has to be made up or what parameters you pass. It only requires a unique resource identifier and probably that it should be self-descriptive, meaning that a client can take further actions based on the returned content (HATEOAS, included links even to itself and proper content type specification).
DELETE
As you want to keep a trace of the deleted resource in some other table, you can either pass data within the URI itself maybe as query parameter (even JSON can be encoded in order to be passed as query parameter) or use custom HTTP headers to pass (meta-)information to the backend.
Sending a complex object (it does not matter if it is XML or JSON) as query parameter may cause certain issues though as some HTTP frameworks limit the maximum URI size to roughly 2000 characters. So if the invoked URI exceeds this limit, the backend may have trouble to fulfill the request.
Although the hypertext transfer protocol does not define a maximum number (or size) of headers certain implementations may raise an error if the request is to large though.
POST
You of course also have the possibility to send a new temporary resource to the backend which may be used to remove the pending payment request and add a new entry to the trace table.
According to the spec:
The action performed by the POST method might not result in a resource that can be identified by a URI. In this case, either 200 (OK) or 204 (No Content) is the appropriate response status, depending on whether or not the response includes an entity that describes the result.
This makes POST request feasible for short living temporary resources which trigger some processing on the server side. This is useful if you want to design some queue-like or listener system where you place an action for execution into the system. As a POST request may contain a body, you can therefore send the POS response within the body of the POST request. This action request can then be used to remove the pending POS request and add a new entry to the trace table.
PATCH
Patch is a way a client can instruct a server to transform one or more resources from state 1 to state 2. Therefore, the client is responsible for breaking down the neccessary actions the server has to take to transform the resources to their desired state while the server tries to execute them. A client always works on a known state (which it gathered at some previous time). This allows the client to modify the current state to a desired state and therefore know the needed steps for the transition. As of its atomic requirements either all instruction succeed or none of them.
A JSON Patch for your scenario may look like this:
PATCH /path/to/resource HTTP/1.1
Host: backend.server.org
Content-lengt: 137
Content-Type: application/json-patch+json
If-Match: "abc123"
[
{ "op": "remove", "path": "/easyPayRequest/12345" }
{ "op": "add", "path": "/trace/12345", "value": [ "answer": "POSAnswerHere" ] }
]
where 12345 is the ID of the actual easypay request and POSAnswerHere should be replaced with the actual response of the POS service or what the backend furthermore expects to write as a trace.
The If-Match header in the example just gurantees that the patch request is executed on the latest known state. If in the meantime a further process changed the state (which also generates a new If-Match value) the request will fail with a 412 Precondition Failed failure.
Discussion
While DELETE may initially be the first choice, it is by far not the best solution in your situation in my opinion as this request is not really idempotent. The actual POS entity deletion is idempotent but the add of the trace is not as multiple sends of the same request will add an entry for each request (-> side-effect). This however contradicts the idempotency requirements of the DELETE operation to some degree.
POST on the otherhand is an all-purpose operation that does not guarantee idempotency (as PATCH does not gurantee either). While it is mainly used to create new resources on the server side, only the server (or the creators of that server-application) know what it actually does with the request (though this may be extended to all operations). As there are no transactional restrictions adding the trace might succeed while the deletion of the pending request entity might fail or vice versa. While this may be handled by the dev-team, the actual operation does not give any gurantees on that issue. This may be a major concern if the server is not in your own hands and thus can not be modified or checked easily.
The PATCH request, which is still in RFC, may contain therefore a bit more semantic then the POST request. It also specifies the ability to modify more then one resource per request explicitely and insist on atomicity which also requires a transaction-like handling. JSON Patch is quite intuitive and conveys a more semantics then just adding the POS response to a POST entity body.
In my opinion PATCH should therefore be prefered over POSTor DELETE.
I have a REST Service and depending on the type of Resource that is being viewed I have certain Operations avialable
So Resource1 supports Operation1 and Operation2
eg:
Resource1/Operation1
Resource1/Operation2
And Resource2 only supports Operation1
eg:
Resource2/Operation1
So if I receive a call for Operation2 on Resource2 (eg: Resource2/Operation2) the server will not raise an error as Operation2 is valid, but internally I don't support it, so should I return a 404 - Not Found or would a 501 - Not Implemented be more accurate or another error code?
You have to think in terms of Resources (that exist and you operate on them) and not Services as in WebServices (which you call to do something).
So every Operation of your REST API is a resource by itself (i.e. to model long running operations or some sort of more complex transaction that are triggered by sending a POST request to the operation resource) - and the URI to that resource is any opaque string, which can be 'Resource1/Operation1' or "/someOther/arbitrary/path",
A 501 means the HTTP server itself has a particular feature not implemented that is require to fulfill the request - i.e. it lacks some technical features - but the resource itself is valid.
But as you've written, your Rest API is not designed to support Operation2 for Resource2 at all, which means that there is no such thing as a "Resource2/Operation2" service resource. Therefore 404 response is the only reasonable choice.
In terms of "how client will interact...", ask yourself: given you are the consumer of a resource (i.e. some arbitrary web site), will you revisit the same URL when receiving a 404? (probably not) will you do when receiving a 501? (probably yes, as you assume the problem has been resolved in the meantime)
TL;DR
return 404
Never return any HTTP 5XX status from you code. It's used by the server to report server internal problem. If you are using that it would be confusing for the application user. So just use 404 for your purpose.
You can use either of them,
its your webapis , depend upon how the client will interact with it.
4xx is for client error and 5xx is for server error
you may use
501 Not Implemented instead of 404 not found
because the resource in url is not there .
OR you can also use
your own custom error code and error message.
or
422 Unprocessable Entity
The request was well-formed but was unable to be followed due to semantic errors.
If the client request by definition is illegal, I would return HTTP 400 Bad Request.
Sending HTTP 400 also implies that the request should not merely be retried, as it is permanently illegal and is not understood by he server.
Only reservation I would have with using HTTP 400 is that it usually seems to be used for malformed requests; i.e. typos and the like. Whether your request is illegal or malformed, is a question of semantics.