Erik Hetzner wrote:
The point is, the state AFTER each request is processed is the same,
but the status code is different. You have argued, w.r.t. DELETE, that
as long as we have the same resource state after the request is
processed, we must have the same response, whatever the previous state
of the resource was (that is, either existing or not existing). But
this is not in the HTTP spec, it is not a principle of REST, and it
has nothing to do with idempotence.
I believe it is a principle of REST as applied to HTTP. Of course, my
last name is not Fielding, so what do I know.
I'm not exactly following what you're saying about "AFTER each request".
Again, here's how I look at it, with two examples; your PUT example
and a caching example.
First, let's establish what a "request" is: url + pre-condition headers
+ HTTP method + message body (some methods requiring a message body
(like POST, PUT) and others not (like GET or DELETE)).
Here's the caching example:
Step 1) A client requests a resource via GET. It has not previously
requested this resource, so it has no ability to include a
If-Modified-Since or If-None-Match precondition header.
--- Client -------------------
GET /some/url HTTP/1.1
Host: foo
--- Server -------------------
HTTP/1.1 200 OK
Etag: "abcdefg"
message body
A 200 OK is returned, along with a new Last-Modified and/or Etag header.
For simplicity, we'll just use the ETag, which the client records
along with the cached version of the resource.
Step 2) A client makes a request for the _same_ resource using the
_same_ url. However, this time the client uses a If-None-Match
precondition in the request. Thus, the "request" itself (as defined
above) is different.
--- Client -------------------
GET /some/url HTTP/1.1
Host: foo
If-None-Match: "abcdefg"
--- Server -------------------
HTTP/1.1 304 Not Modified
The server replies with a 304 Not Modified reply. The server can return
a different status code because the request is different. It will
always return this 304 Not Modified reply until the resource changes
(for that specific request). It will always return a 200 if the
precondition header is not included. The server is unable to escape
these rules.
The point is, notice that the Request has changed. It doesn't matter
how many times you throw the first request at the server, it will always
return 200. Likewise, the second request will always return 304, until
the resource's state changes.
This exact same evaluation can be applied to your PUT example, except of
course the server replies with Precondition Failed (which according to
the spec is an appropriate response for a PUT method under the
circumstance you describe). It will always return 412 after the first
PUT for identical requests (because the first request modifies the
resource, and thus allows the server to return a different status code
for the same request).
That's the core of my argument: A status code should always be the same
for an identical request so long as the resource has not change.
If the server is coded to return a different status code for an
identical request when the resource state has not changed, then that is
wrong. The only liberty a server has to return a different status code
for the same request is when the resource has changed.
A logical conclusion of this principle would be that PUT should never
return a 201 Created, because this depends on what the state of the
resource was before the request was received. PUT should always return
200, because the state of the resource previous to the request is
irrelevant.
You're right. PUT should never return 201 Created. The reason? 201
Created should have a Location: header sent in reply. The client
doesn't need a Location: header, since it already knows the location the
resource.
The client doesn't care whether the resource was _newly_ created, or
just _updated_. What does the client care? This goes back to the same
argument about DELETE. So long as the state change for the resource
occurred, that's all the client cares about, not whether it's newly
created or not. What is newly created anyway? And, what does the
client care to know the difference?
The 201 Created status code is only useful when the resource's URL is
not known by the client. Therefore, 201 Created is only applicable to
the POST method, because POST doesn't know what the url is for the
resource in its message body.
A successful PUT can return 204 No Content for an immediately successful
state change, or 202 Accepted for one that will occur in the future.
There are no other 2xx response codes appropriate for a PUT operation.
Again, this is not arguing HTTP spec, as the HTTP spec is quite a bit
more lenient than the rules I've suggested. And, the rules I've
suggested may not strictly be REST rules, as I believe Fielding was
describing REST as an architectural pattern that is applicable to
protocols other than just HTTP.
However, if you follow the HTTP spec and you apply the REST principles
to it, I believe what you end up with is this logical mapping of what
HTTP status codes can be returned for which HTTP methods. For 2xx only,
it goes:
GET => 200, 206
PUT => 204, 202
POST => 201, 202, 205
DELETE => 204
HTML is an abomination to the HTTP spec. So, I'm not arguing what is in
current use, more what it ideally should be.
No more replies from me. I feel I've hijacked the thread, and we're
obviously way off RESTlet discussion. ;)
Adam