Question

I implemented an API that renames a company as follows:

PUT /companies/A
{
  "name": "B"
}

will return HTTP 301 with the Location header pointing at the company's new URI: /companies/B.

How can I make this operation idempotent with and without If-Match headers?

  1. Without If-Match header: if a user tries to rename a non-existent company, I'd expect the server to return HTTP 404 but I can't do so because then legitimate rename operations wouldn't be idempotent (they'd return 301 the first time, and 404 on subsequent invocations). This is problematic because I want clients to be able to differentiate between a failed renames (the company doesn't exist) versus a rename that had already taken place.

  2. With If-Match header: if the company's ETag depends on the company name, then subsequent rename operations will fail because the precondition no longer holds. Again, this makes it seem that the operation failed when in fact it already took place.

Was it helpful?

Solution 2

The PUT operation succeeds and should return a 200 or 201. Subsequent requests for the same resource should return a 301 with the appropriate response body indicating the URI of the new resource.

404 should only be for resources that truly can't be found, i.e. companies that don't exist and never have.

As noted in the protocol, idempotence doesn't mean that the call returns the same thing all the time. It means there are no side effects. Also, idempotence isn't applicable under error conditions, which anything other than 2xx (like 301) is.

I really do admire the commitment to getting it right by the spec, but as with all things, it is subject to interpretation.

OTHER TIPS

OK, this is two years old, but I'm going to answer it in case someone else stumbles upon it as I did.

The short answer is that, from HTTP point of view, renaming (moving) resources is not idempotent, and you should have used POST instead of PUT.

The long answer: PUT is a "create-or-replace" operation, defined by RFC 2616 as follows (emphasis mine):

The PUT method requests that the enclosed entity be stored under the supplied Request-URI.

RFC 7231 (which at the time this question was asked existed only as draft), puts it more clearly:

The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload. A successful PUT of a given representation would suggest that a subsequent GET on that same target resource will result in an equivalent representation being sent in a 200 (OK) response.

Because a successful rename will result in the resource being available at a different location, PUT is not applicable.

PS. Probably you could have made this work with PUT by including some sort of unique identifier for the company, regardless of it's name or other attributes, in the request body, that would allow you to detect previous renames and issue an appropriate redirect. Nevertheless, I think it is going against the protocol, and POST would have been more appropriate.

I don't think a 3xx makes sense here. The PUT operation succeeded, so it should return a 2xx. 301 implies that the resource isn't where the requester thought it was.

Generally, I keep being amused about people not using MOVE when they actually want MOVE :-)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top