Wednesday, May 23, 2012

Grails and REST

I've always been a fan of REST (REpresentation State Transfer) APIs. As part of a simple application I am working on in my spare time I just implemented some basic REST interfaces (at least the URIs) for the ajax calls involved. I thought I would share some specifics of my experience as note for myself and for anyone else working this out as well.

Implementing REST in Grails 2 is not hard, but not overly documented. The documentation is brief. I wandered far and wide from their simple example trying to resolve issues, and then made my way back.

Implementing REST URIs in Grails requires changing the URL mappings for that resource and the controller you need to handle these requests.


For my Page resource my UrlMappings.groovy needed to contain this:

static mappings = {
class UrlMappings {
    static mappings = {
        ...
        //page resources
        "/pages" (controller:"page", action:"list")
        "/pages/$id?" (resource:"page") 
        ...
    }

Note the use of the resource parameter. In practice this is a shortcut for specifying a standard HTTP action to controller method mapping for CRUD operations, such as GET mapping to show(), POST mapping to save(), PUT mapping to update() and DELETE mapping to delete(). Any collection resource (in the example above, '/pages') needs to be independently declared.
A fast and loose controller implementation might look like this:

package myapp

import grails.converters.JSON

class PageController {

    def list() {
        render Page.list(sort:"pageOrder") as JSON 
    }

    def show() {
 render Page.get(params.id) as JSON
    }
 
    def update() {
        def page = Page.get(params.id)
        bindData(page, params, excludes:'id')
        render page as JSON
    }
 
    def delete() {
        Page.get(params.id).delete()
        render(status:204)
    }
 
    def save() {
        def page = new Page()
        bindData(page, params, excludes:'id')
 page.save()
 render(model:page,status:201) as JSON 
    }
}

There is a trick to testing RESTful URL mappings. The out of the box test helpers like assertForwardUrlMapping() do not allow you to specify an HTTP method, so it needs to be set separately prior to the test:

...
void testPageDelete() {
    webRequest.currentRequest.setMethod("DELETE")
    assertForwardUrlMapping("/pages/1", controller:'page',action:'delete')
}
...

Let me know how you go.

No comments:

Post a Comment