301 vs 303

Warning: This blogpost has been posted over two years ago. That is a long time in development-world! The story here may not be relevant, complete or secure. Code might not be complete or obsoleted, and even my current vision might have (completely) changed on the subject. So please do read further, but use it with caution.
Posted on 02 May 2012
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:

  1. We want to notify the user that the operation has been completed.
  2. 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 :)