Wednesday, December 17, 2008

URI Design for REST

Introduction

After making the example to fit a restful design in struts 1.x, I began investigating which rules exist to create a URI. A URI should be a simple representation but as I we all know, making things simple is the hardest thing to do.
I found some blogposts talking about this topic:
The initial take is how Ruby handles the URI design because there seems to be discussion about using GET /resource/1;edit or GET /resource/1/edit.
I also think there are more cases that need to be handled, for example
  • doing a search for a resource,
  • performing a calculation,
  • showing partial information of a resource,
  • showing an aggregated view of multiple resources,
  • ...
Also, a URI should always be accompanied with its http method to know what the function is (the function should be a sentence where the http method is the verb and the URI the direct object an example would be "I get resource 1").
In this blog post I won't take in account that a normal browser request can't change the Accept header and can't send any thing else than GET and POST. These shortcomings will need to be emulated in some way.

Identified resources

GET /resource/1 : Showing a single resource with identifier 1. We could also do GET /resources/1 which would be more of a search but since it only returns one item I agree with Robert Hahn that the first representation is the best.Using the different http methods we get:
  • POST /resource/1 : Add a resource and use identifier 1 (in a lot of cases, this will make no sense since the identifier will be created by the server or database)
  • PUT /resource/1 : Update the resource with identifier 1
  • DELETE /resource/1 : Delete the resource with identifier 1
Now we get to the difficult one. How do we get to the page where we will update the attributes of the resource. The links I provided above give the following solutions for this: GET /resource/1;edit (apparently from Ruby), GET /resource/editable/1 and GET /resource/1.form. All of these just don't seem to feel right. I think the editable representation is just a representation as another and should be handled as such. Thus giving an Accept header as text/form.

A newly created resource

  • GET /resource : Get the input form to create a new resource
  • POST /resource : Create a new resource. The result should be a redirect to GET /resource/new_id
Put and delete do not make sense for resources that do not yet exist, thus an identifier should be given in that case.

Searching a resource

GET /resource?field=searchvalue&field2=searchvalue2 : Searching is why request parameters exist so use them for this. A very complex search could be handled in the same way as a calculation.

Multiple representations of the same resource

Multiple representations should be retrieved using a different Accept header. Problem is that in the browser, the request header can only be set when doing an AJAX request. For links, you are stuck with what the browser provides.

Aggregated view of multiple resources

I think this is just a different view (=Accept Header) for a resource. Thus information of a person with his address would be GET /person/1/address with an Accept header text/vnd.app.person+address.
What if you want to see or edit a person with his address and orders on one screen? I assume something of the following: GET /person/1/address+orders would do. The address and the orders are related to the person and not to each other thus GET /person/1/address/orders would be wrong.

Calculation

Calculations are a two step process. This first is creation of the calculation, the second retrieval of the result. Thus a POST /calculation would be redirected to return a GET /calculation/123 . If this calculation is long running, the GET could be polled to check if the result is already available. The nice thing of this is that you could bookmark the result or send it to someone else without redoing the calculation.
This solution also has its impact on the server side. The result of the calculation will need to be stored somewhere or a 410 Gone should be returned (this could also be a 404 if there was no result with the given id but how are you going to know this?)

Other REST issues to think about

  • Governance of URI's: As for SOAP web services, REST web services should be reusable. A certain amount of governance is needed to achieve this across projects. I found the following nice article on infoq. It doesn't give any satisfying solution but still a nice read.
  • How to get links in your response in an easy manner?
  • Maybe I should see REST less as "returning a presentation" and more as "providing a service". It needs a smart client to talk to services, a dumb client (=browser) can only show a representation. In this, I follow the article of Dave Thomas.
  • Circumvent the problem that in a browser could can't set the Accept header (or any other header) in the request. Same for PUT and DELETE, this is supported by google with a hidden field in a POST request that is on the serverside translated.
  • Add your own ...

Conclusion

URI design is not that easy. Once you make a decision on how each function is represented by a URI, you should stick with that style for your entire site. Having a clear set of guidelines to create URI's is more important than selecting THE BEST style.

0 reacties:

 
The opinions expressed in this blog are those of Jeroen Wyseur and may not reflect the opinion of his employer or any customer of said employer.