Struggling with REST in Grails

I’ve been trying to implement RESTful web services with Grails. Grails turned out to have some nifty features. But I also struggled a lot, and got frustrated too. I’ll tell you what I found out about content negotiation and mapping different HTTP methods. But first, a short recapitulation of RESTful services.

REST, a short recapitulation

REST is a lightweight way of implementing web services. It basically assumes that there are two important concepts for a web service:

  • Resources (data, documents, content)
  • Operations (reading, writing, deleting)

REST conveniently maps those two concepts on the corresponding concepts in the http protocol. Each resource is mapped on a URL.

http://mynaughtydomain.xxx/myapp/people/myself
would refer to a document (e.g. an html page, or a snippet of xml, or both) about “myself”.
http://mynaughtydomain.xxx/myapp/people
would refer to a list of all people known in myapp
http://mynaughtydomain.xxx/myapp/people/johnmccain/photographs
would refer to a list of all photographs of a certain person
http://mynaughtydomain.xxx/myapp/people/johnmccain/photographs/inatigersuit.jpg
would refer to a photograph you don’t really want to see.

Nothing special so far. The URLs have a tree structure. And a GET-request to a URL returns a web page. But there’s more.

A PUT-request to a URL sends information to the web application. The information is not just stored on a web server, but actually read and processed by the application. So, if I PUT an XML-document on http://mynaughtydomain.xxx/myapp/people/sarahpalin, not only mrs Palin will be astronged to mynaughtydomain. She will also receive a confirmation mail. And her credit card will be charged for $10000 too.

Luckily for her, she can also send a DELETE request. But that won’t give her her money back.

Content negotiation

URL mappings in Grails are defined in grails-app/conf/UrlMappings.groovy. The following pattern will provide a mapping for viewing photographs.

"/people/${person}/photographs/${photo}.jpg" {
    controller = "photo"
    action = "show"
}

Requests that match the pattern will be sent to the show action of the PhotoController class. ${person} and ${photo} can be received just as if they were normal http request parameters. Within PhotoController.action, we can refer to them with params.person and params.photo.

Now suppose that for some reason, I do not want to return a JPG-file, but an XML-file with information about the photograph. It would be easy to change the above code into

"/people/${person}/photographs/${photo}.xml" { // don't do this, it doesn't work!
    controller = "photo"
    action = "show"
}

The first URL pattern works. The second does not. Why?

Grails uses a technique called content negotiation, to return a resource in the format that is required by the client. Each http request has an accept header. The request header tells the server, what document formats it is allowed to send back. A browser request includes text/html and application/xml in the accept header. A client that only understands json, would only include the accept header application/json. This way, the server can send the same resource in different document formats. The same URL http://mynaughtysite.xxx/myapp/people will either send html or xml, based on the accept header. Or it will send an error, that it doesn’t know what the heck json is.

Unfortunately, browsers accept headers are fixed. Browsers will always prefer to receive xhtml or html. And there are a number of fixed second choices. So, in a browser, if I ask for http://mynaughtysite.xxx/myapp/people, I will always get the html version of the list and never the xml version. But Spring made a nifty feature to solve this. If you cannot use accept headers, you can append the URL with a format extension. So

http://mynaughtysite.xxx/myapp/people.html
will have the same effect as GETting http://mynaughtysite.xxx/myapp/people with text/html as the accept header
http://mynaughtysite.xxx/myapp/people.xml
will have the same effect as GETting http://mynaughtysite.xxx/myapp/people with application/xml as the accept header

grails-app/conf/Config.groovy contains the following list

grails.mime.types = [ html: ['text/html','application/xhtml+xml'],
                      xml: ['text/xml', 'application/xml'],
                      text: 'text/plain',
                      js: 'text/javascript',
                      rss: 'application/rss+xml',
                      atom: 'application/atom+xml',
                      css: 'text/css',
                      csv: 'text/csv',
                      all: '*/*',
                      json: ['application/json','text/json'],
                      form: 'application/x-www-form-urlencoded',
                      multipartForm: 'multipart/form-data'
]

html and xml are in there as mime types. That is why in the second mapping, we must NOT explicitly specify .xml. Spring’s content negotation will make sure that .xml and .html are mapped correctly. jpg is not in the list of mime types. That’s why the first mapping works as is.

To implement different formats in your controller action, you can inspect the value of request.format. Or, even nicer, you can use withFormat. The first format specified within withFormat is the default format. Note that instead of “html”, request.format may also have the value “form”, which means more or less the same.

HTTP methods

The two specified mappings, map ALL http requests to a single controller action. GET, POST, PUT and DELETE are all mapped to the same method. We can do a switch/case statement within the method. But that is evil. Instead, we can also change the mapping to this:

"/people" {
    controller = "person"
    action = [GET: "list"]
}

"/people/${person}" {
    controller = "person"
    action = [GET: "show", DELETE: "delete", PUT: "chargethebitch"]
}

Requests sent to the application will work as expected. However, the links generated by grails in your scaffolded pages WILL NOT!!!

Grails provides <g:link>, <g:actionSubmit> and <g:form> tags that reverse map names of controllers and actions to URLs. In the tags, you specify the controller and action you want to link to. At runtime, Grails replaces the controller/action-specification by the URL you specified in UrlMappings. Or at least that is what it should do.

If you use request method specifications in your mappings, forward mapping URLs to controller methods works just fine. But reverse mapping controller and action names back to a URL does not! Luckily, according to Graeme Rocher, it’s not a bug, it’s a feature!.

There are at least two ways you can work around this:

  • Use the request method mappings, but do not use the <g:link>, <g:actionSubmit> and <g:form> tags. Instead, use normal html <a> and <form> tags, and explicitly specify the URLs. Since you have thoughtfully considered your RESTful API, the URLs are logical, and are unlikely to change.
  • Do not use the request method mapping, but use a switch/case in the corresponding controller methods. This leads to ugly code.

Or of course, you can implement your own version of the three tags. Grails puts a spring bean grailsUrlMappingsHolder in the application context that might be useful.

Summary

Grails has some nifty features for implementing RESTful services, but it definitely has its rough edges too. Be aware of this, when you’re implementing REST.

Advertisements

Tags: , , , , ,

One Response to “Struggling with REST in Grails”

  1. http://mensengagementrings.ca Says:

    Some genuinely great information, Gladiolus I found this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: