9

I am designing a RESTful API that is managing favorites. Each favorite resource can contain details of two items that are considered as part of the favorite.

 HTTP POST /favorites

 {  "item1" : "ball",
    "item1-ID" : "1",
    "item2" : "bat",
    "item2-ID" : "2"
 }

Please excuse the rudimentary JSON payload. The focus is however on the semantics of the POST

The above POST method creates a new favorite resource (that contains a ball (ID 1) and a bat (ID 2))

My question is with regard to the expected behavior when the same POST request is sent twice. The first request will create a favorite (as expected). What should happen when the second request is sent?

1) Signal an error with 409

2) Signal a success with 201

1) is not idempotent (as POST is), while 2) makes POST idempotent.

Which is the correct approach?

Sirish Renukumar
  • 1,509
  • 2
  • 13
  • 16
  • One of the tags is Java… this has nothing to do with Java – Krusty the Clown Oct 21 '15 at 02:02
  • If I were a programmer using this, I'd prefer an error. I can choose to ignore an error, but I can't know that success means something different. – markspace Oct 21 '15 at 03:01
  • One of many many [PUT vs POST](http://restcookbook.com/HTTP%20Methods/put-vs-post/) resources; this one says "PUT is idempotent, while POST is not" – Stephen P Oct 21 '15 at 03:08

5 Answers5

4

You are thinking about this in the wrong way. If a POST creates a new resource then the resource should have a URL that identifies it (e.g., http://.../favorite/1). When the second POST with the same payload happens, is a new resource created? If so, then you have two separate resources with unique URLs. If you application does not create a new resource, then the second POST would return the same URL as the first one.

Edit

The POST section of RFC7231 does not prohibit it from being implemented in an idempotent manner. It does provide some guidance though:

  • If a POST creates a new resource, then it SHOULD send a 201 (Created) response containing a Location header identifying the created resource
  • If the result of a POST is equivalent to an existing resource, then the server MAY redirect the UA to the existing resource by returning a 303 (See Other) with the appropriate Location header

POST is not required to change the state of a resource. However, GET and HEAD are required to be idempotent. I'm not aware of any method that is required to change state on the server.

Personally, I implement resource creating POST methods as returning a 303 redirect to the canonical URL for the resource as a matter of practice. I wasn't aware of the RFC distinguishing status codes for resource creation and re-use responses. The benefit of this approach is that it removes the need to include the created resource in the POST response and the GET to the canonical URL will cache the response in any intermediate caches that may be present. It also allows for the implementation of intelligent clients that do not retrieve the response and use the canonical URL (from the Location header) instead.

In your case, I think that I would adopt the 303 approach provided that a favorite has a URL of it's own; 201 would be strange if it did not. If there is very good reason for distinguishing the creation and re-use cases in the response, then a 201 when the new resource is created and a 303 when it is reused is appropriate. What I would not do is to return a 409 unless your application requires you to deny the creation of a resource that already exists.

Community
  • 1
  • 1
D.Shawley
  • 58,213
  • 10
  • 98
  • 113
  • My query was with regard to whether the implementation can allow POST to be idempotent? My application considers the second request as redundant (since the required items are already added as a favorite). I therefore wanted to understand if returning 201 is acceptable (since this implies that multiple POST requests can be sent without resulting in any new state change on the server which makes the POST idempotent – Sirish Renukumar Oct 21 '15 at 10:41
1

To me, as you pictured it, it should create a new favorite. But this feels strange for almost any application. What if:

  • your favorites could be defined as non-repeatable if they are exact copies
  • you could send an ID with the favorite (not a good idea, as it'd be client-based), and insert or update based on that
  • you could send some other unique field and use that to define whether it is a "create" or an "update" (ie. "favorite name" or "position in menu")

As you see, it all depends on your application.

Maybe you can get some ideas from this article I happened to write some time ago: A look into various REST APIs . Don't miss the summary at the bottom.

jjmontes
  • 24,679
  • 4
  • 39
  • 51
0

What should happen when the second request is sent?

It all depends on your implementation . Say if you have some logic in your controller that checks if ID 1 and ID 2 exist in the data base then update the value else create the new record. The whole thing is not depended on POST request.

AND FYI HTTP response code for success is 200 and not 201.

Your current approach is not correct and the question is not a JAVA question

Albert Pinto
  • 392
  • 2
  • 6
  • 17
  • 3
    201 is HTTP success result code for "Created", a valid response code especially in REST scenarios. Also, the comment about Java should have been a comment, I kindly suggest you to remove it and optionally add it as a comment to the question. And perhaps remove the "your current approach..." sentence entirely ;). – jjmontes Oct 21 '15 at 02:48
0

In general, it depends on you, how you want to design the underlying system. In most cases, POST creates a new resource (Yes, there are instances when you use POST and not create anything new. For example, you have a use-case where in you want to search on a very long list of the parameters and GET might not be the way!. This is completely acceptable and not a violation of REST principles.)

Commenting on your specific case,

What should happen when the second request is sent?

  • You should ask the following questions to yourself.

    1. Is having a duplicate request (a request with the same payload/headers/time-stamp etc.) a "different" request for your system ? If YES - treat it normally as if it's not duplicate. I assume that the next systems to which the request is going are also understanding this as a new request, otherwise they could fail and flag error. For example, MySQL could throw Duplicate Primary Key error (Refer: Error Code: 1062. Duplicate entry 'PRIMARY')

    2. If the answer to (1) is NO - Is the duplicate occurrence of this request an expected behaviour of the clients ? (For example, you tend to get duplicate requests from mobile if it's intermittently getting the network.) If YES - return 200 OK. Note the idempotency here.

    3. If the answer to (2) is NO - throw 409 conflict with a custom error-code which client can handle and react accordingly. I would also investigate the unwanted behaviour of client on throwing the duplicate requests and fix it if that's doable.

Community
  • 1
  • 1
Punit Mehta
  • 11
  • 1
  • 2
0

Duplicate idempotent POST requests should return the previously created resource in response if the status code of the response was 200. Idempotency Enforcement Scenarios

Phil
  • 131
  • 2
  • 4
  • Link is broken, this is a new one: https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-02#section-2.6 – caiolopes Jan 30 '23 at 12:54