Hypermedia is the technical term used to describe how web pages (resources) are linked or connected. The author of RESTful Web Services calls it connectedness: how the server guides the client from one application state to another by providing links. A good example of a well-connected application is Wikipedia. It's very powerful when you could pick a random page and click through every entry on Wikipedia without ever editing your browsers URL. Performing a search on Google is another great example, as it wouldn't be very useful if the search result didn't include any links. Without these links, the client must know and create predefined rules to build every URL it wants to visit.
So what do links on a website have to do with REST Services? REST was built on the foundation of the web and just because your not returning HTML doesn't mean you shouldn't create relationships between your resources. In fact, it's amazing how powerful embedding links in your responses can be. For instance, it prevents tight coupling between your clients and services as the clients don't have to construct or even know the exact URLs because the services are providing them.
Let's use Twitter as an example. Assume the following is the URL to get the 20 most recent tweets for my account:
http://api.twitter.com/1/statuses/user_timeline.json?screen_name=jlorenzen
And here is a shortened fictitious response:
[ { "text": "Finished watching Battlestar Galatica" "id": "55947977415667712" "url": "http://api.twitter.com/1/statuses/show/55947977415667712.json" }, { "text": "Started watching Battlestar Galatica" "id": "45947977415667712" "url": "http://api.twitter.com/1/statuses/show/45947977415667712.json" } ]
Notice each tweet includes a direct URL. Now clients can use that URL value verses constructing it, and if it changes in the future, clients don't have to make any changes.
Example using Jersey
So what is the best way to include links in your REST Services? Since I use Jersey on a daily basis, I'll go ahead and show an example of how to embed links in your responses using Jersey. Most likely, any REST framework is going to provide the same kind of features.
Using the twitter example above, the User Status Service with links may look something like this (using groovy):
import javax.ws.rs.GET import javax.ws.rs.Path import javax.ws.rs.Produces import javax.ws.rs.QueryParam import javax.ws.rs.core.Context import javax.ws.rs.core.UriInfo import com.test.UserStatuses @Path("/statuses") class StatusesResource { @Context UriInfo uriInfo @GET @Path("/user_timeline") @Produces(["application/json", "application/xml"]) def UserStatuses getUserTimeline(@QueryParam("screen_name")String screen_name) { def statuses = getStatuses(screen_name) statuses.each { it.url = "${uriInfo.baseUri}statuses/show/$it.id" } return new UserStatuses(statuses: statuses) } }
In this simple example, Jersey injects the UriInfo object which we use to get the baseUri of the request. It's really that simple.
Potential Issues
Well for some it may not be that simple. For example, we discovered an issue when using a reverse proxy (apache httpd). In our production environments, we typically setup apache on port 80 to proxy a localhost JBoss on port 8080. Unfortunately, in this setup the UriInfo.getBaseUri() returns localhost:8080 and not the actual original URL the client used; which is obviously not good. Now if you don't use a reverse proxy then no worries. However, if you do or might potentially in the future, a easy solution seems to be to set the Apache Proxy module option ProxyPreserveHost to On. Setting this to On and restarting Apache seems to fix the issue.
JSONView Firefox Plugin
Once you've started embedding URLs in your REST responses, you might find it useful to install the JSONView firefox plugin. It's got some really slick features like formatting the JSON and creating clickable links for URLs.