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?
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.
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.
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 successfulPUT
of a given representation would suggest that a subsequentGET
on that same target resource will result in an equivalent representation being sent in a200
(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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With