Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

REST: Prevent creation of duplicate resources via conditional POST?

Tags:

rest

post

I've been searching for best practices for preventing the accidental creation of duplicate resources when using POST to create a new resource, for the case where the resource is to be named by the server and hence PUT can't be used. The API I'm building will be used by mobile clients, and the situation I'm concerned about is when the client gets disconnected after submitting the POST request but before getting the response. I found this question, but there was no mention of using a conditional POST, hence my question.

Is doing a conditional POST to the parent resource, analogous to using a conditional PUT to modify a resource, a reasonable solution to this problem? If not, why not?

The client/server interaction would be just like with a conditional PUT:

  1. Client GETs the parent resource, including the ETag reflecting its current state (which would include its subordinate resources),

  2. Client does a conditional POST to the parent resource (includes the parent's ETag value in an If-Match header) to create a new resource,

  3. Client gets disconnected before getting the server response, so doesn't know if it succeeded,

  4. Later, when reconnected, the client resubmits the same conditional POST request,

  5. Either the earlier request didn't reach the server, so the server creates the resource and replies with a 201, or the earlier request did reach the server, so the server replies with a 412 and the duplicate resource isn't created.

like image 624
Chris Toomey Avatar asked Apr 09 '13 20:04

Chris Toomey


1 Answers

Your solution is clever, but less than ideal. Your client may never get his 201 confirmation, and will have to interpret the 412 error as success.

REST afficianados often suggest you create the resource with an empty POST, then, once the client has the id of the newly created resource, he can do an "idempotent" update to fill it. This is nice, but you will likely need to make DB columns nullable that wouldn't otherwise be, and your updates are only idempotent if no-one else is trying to update at the same time.

According to ME, HTTP is flaky. Requests timeout, browser windows get closed, connections get reset, trains go into tunnels with mobile users aboard. There's a simple, robust pattern for dealing with this. Unsafe actions should always be uniquely identified, and servers should store, and be able to repeat if necessary, the response to any unsafe request. This is not HTTP caching, where a request may be served from cache but the cache may be flushed for whatever reason. This is a guarantee by the server application that if an "action" request is seen a second time, the stored response will be repeated without anything else happening. If the action identity is to be generated by the server, then a request-response should be dedicated just to sending the id. If you implement this for one unsafe request, you might as well do it for all of them, and in so doing you will escape numerous thorny problems: successive update requests wiping out other users' changes, or hitting incompatible states ("order already submitted"), successive delete requests generating 404 errors.

I have a little google doc exploring the pattern more fully if you're interested.

like image 141
bbsimonbb Avatar answered Nov 13 '22 07:11

bbsimonbb