301 vs 303
Tagged with: [ http ] [ rest ] [ see other ]
During a presentation I gave yesterday about REST, there was a discussion about redirection (more detailed, a redirection from a queue to the actual resource during asynchronous operations). During this presentation (and blog-post), I’m using a 303 HTTP status code to indicate that the operation has been completed and that the created resource can be found at another URI. So in essence, it makes sense to use a 303. At least to me, and quite possibly the rest of the world too.. But this triggered a side-discussion on which HTTP status code to use, and the more I think about it, the more complex it believe this problem actually is.
This is the 303 code specification in the HTTP/1.1 RFC:
The response to the request can be found under a different URI and SHOULD be retrieved using a GET method on that resource. This method exists primarily to allow the output of a POST-activated script to redirect the user agent to a selected resource. The new URI is not a substitute reference for the originally requested resource. The 303 response MUST NOT be cached, but the response to the second (redirected) request might be cacheable.
OK, they are implying that you request “something”, but instead of that “something”, you are being redirection to “something else”. The 303 response should not be cached, but the location the 303 points to, can be cached. This makes sense and still is valid in redirecting a user to the actual resource after the job in the queue has finished. They even provide a use-case, where a POST to for instance a form, will redirect the user to another page. Key here, I think at least, is the fact that the new URI is not a substitute for the old URI. In other words: it is not the same resource, but on another place. The URI points to a completely different resource.
The next sentences:
The different URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).
So we should return the actual resource through the “Location:” field, and we should return a bit of hypertext with a href or rel to the new uri (there can be multiple!).
I still think it makes sense to use this code since we are doing two things after completing our asynchronous operation:
- We want to notify the user that the operation has been completed.
- We want to redirect the user to the OUTPUT of the operation (ie, the newly created resource).
Both can be fulfilled with a 303 response.
Now, the suggestion came that you should 301 for this behavior instead of 303. So let’s read the docs:
The requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs. Clients with link editing capabilities ought to automatically re-link references to the Request-URI to one or more of the new references returned by the server, where possible. This response is cacheable unless indicated otherwise.
It states that a 301 is a permanent redirection of the resource to another location (Like a “we’re moved!” sign). However, the actual resource we are asking when doing a /queue/12345 is not the resource it created, but is the actual queue-task itself. And that queue-task hasn’t moved! When we do a GET /queue/12345 and return a 301 Moved -> /my/new/resource, it implies that this data was originally found on /queue/12345, but this was never the case! The rest of the sentences aren’t really important in deciding between a 301 or a 303.
The new permanent URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).
Again, this is the same as with a 303, so nothing strange here.
If the 301 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.
This is probably here to make sure that we don’t do a non-safe action to a resource which might have moved in the meantime. But I think this should be handled with etags and if-(not-)modified headers anyway. By the way, there is a draft of a status code that makes it mandatory to use conditional headers when accessing or modifying resources (428 Precondition Required*).
So in conclusion: I’m still not convinced, at least, according to how I interpret the http spec, that we should use 301 here, instead of 303. If you see it differently (and I know some of you will :p), I’d like to find out why a 301 is better in your view. But maybe the most important thing about this discussion, and thus this blog-post, is that interpretation of the rules can be difficult on occasion, and maybe the rules are ambiguous enough that there are no clear winners on some of these discussions. However, it’s good to discuss these things, it makes the whole REST & HTTP way thinking stronger in the end, which in a API-centric world we are entering is definitely not a bad thing :)