On idempotence in HTTP
Today I came across a blogpost arguing idempotence of the HTTP DELETE
method, and how this works in practice.
The blogpost itself is rather confusing, as it corrects and changes itself in a few edits, but the last conclusion as of 11am today is as follows:
When perfoming a HTTP DELETE method on a resource that succeeds, and afterwards performing the exact same HTTP DELETE method again, the HTTP status code should not change.
This conclusion is incorrect, and frankly it was suprising to see that both on that blog, as well as the reddit thread people were confused about the correct answer here.
Hence this post.
HTTP and Idempotence
The following HTTP methods are all considered idempotent:
- PUT
- DELETE
- GET
- HEAD
- OPTIONS
- TRACE
Httpbis defines idempotence as follows:
Idempotent methods are distinguished because the request can be repeated automatically if a communication failure occurs before the client is able to read the server’s response. For example, if a client sends a PUT request and the underlying connection is closed before any response is received, then it can establish a new connection and retry the idempotent request because it knows that repeating the request will have the same effect even if the original request succeeded. Note, however, that repeated failures would indicate a problem within the server.
To be fair, this definitition alone is not entirely clear on wether the response to subsequent requests needs to be identical.
However, this can be inferred from other parts of the specification.
A much clearer example of an idempotent request with varying responses is PUT. See for example the following (simplified) sequence of requests.
PUT /resource HTTP/1.1
If-Match: "1111-1111"
HTTP/1.1 200 OK
ETag: "2222-2222"
PUT /resource HTTP/1.1
If-Match: "1111-1111"
HTTP/1.1 412 Precondition Failed
ETag: "2222-2222"
As you can see the second request, although identical, resulted in a different HTTP response. This is the only correct response.
A server can not remember if a particular client made the request before, because of the state-less nature of HTTP.
This behavior also extends to DELETE
. DELETE
with an If-Match
header must
return 412 Precondition Failed if the resource was already deleted.
If no If-Match
header was provided, the server must return 404 Not Found
,
or in some situations 410 Gone
if the server kept track of deleted
resources.
Accoding to Nicoon on reddit, this was even incorrectly stated in O’Reilly’s RESTful Web Services Cookbook. An excerpt:
“The DELETE method is idempotent. This implies that the server must return response code 200 (OK) even if the server deleted the resource in a previous”
I guess this is just a subtle reminder that beceause it uses dead trees as medium, it doesn’t necessarily it’s correct ;).
The book implies with that statement that a REST server must actually keep
track of every previously deleted resource, just so it can return a 2xx status
code and pretend that the DELETE
succeeded again. This is absolutely false.
tl;dr
When a HTTP method is idempotent, this means that when you do a HTTP request once, the outcome is the same as performing the identical HTTP request more than once.
Outcome in this sentence does not refer to the literal HTTP response, which
is usually different anyway as most HTTP servers send back a Date:
header.
Outcome refers to the state of the resource on the server. If I do the same
DELETE
more than once, the first DELETE
that comes through will return
a 2xx
status code, and any subsequent DELETE
will return a 4xx
status
code because the resource has already been removed.
On HTTP 200 vs 204
This was also addressed in the blog post, and I felt it was worth explaining the difference here too.
204 No Content
is often used as the standard response to a successful
DELETE
. However, 200 OK
is also a perfectly valid response code.
The only different between 200 OK
and 204 No Content
is that with 200
some response body is expected, and with 204 No Content
no response is
allowed to be sent back.
So it is also perfectly acceptable to send back a 204
as a response to a
PUT
or POST
request. There’s also nothing in the specification that seems
to argue against the idea of using a 204
as a response to a GET
request,
As long as the Content-Length
of the response is 0.
Comments
Anna Filina •
I agree with your point. It's actually easy to argue this with the following 3 requests (assume PUT is for "modify" operations):
PUT /resource/1
DELETE /resource/1
PUT /resource/1
It would make absolutely no sense in the 3rd request to send a 200 OK, because the resource is clearly not available.
Evert •
Bit of an odd example though, because PUT is not strictly for updates, it's also used for creation of new resources. So the last PUT could also return a 201.
Anna Filina •
It's not strictly for updates. I just used that scenario as an example to further support the point.
gggeek •
Strongly agree. We should use PATCH for updates, and only use PUT for create :-)
Anna Filina •
Support for PATCH is still not widespread. I need to support some very old tech in most of my projects, so that's usually not an option.
willdurand •
PATCH is tricky to use as you should send a **diff**, not just a set of key/values to update. Actually, a better alternative would be to PUT specific parts of your resource (e.g. PUT /resources/1/price)
gggeek •
I am not against using sub-urls for parts of the resource, but it poses a problem wrt caching reverse proxies: when I publish a new version of resource 1, if I allow usage of sub-urls even for GETs (which is name for smaller payloads/faster interfaces) I am now forced to tell to the reverse proxy to purge resources/1/* as well as resources/1 - and not all of them might be smart enough to support that
willdurand •
Use "sub-urls" for write only, not read.
VK •
Don't we use POST for creation of new resource ? PUT or PATCH for updates. The difference is that PUT request has the complete resource with modifications and PATCH with only parts of the resource that has modifications.
Evert •
POST should only be used in cases where the client can not, or is not allowed to determine the url of the new resource
Marten Gajda •
This doesn't seem to be true any longer. https://tools.ietf.org/html... explicitly states that a server is allowed to return a 2xx response if the outcome would be the current state on the server:
So if you request a DELETE on a non-existing resource, the server can actually return 200 OK.
The same would be true if you PUT the same resource twice.
Evert •
That's super interesting! I had no idea that this was the case. I really like this