Code for this blog can be found on github at
https://github.com/galapagosfinch

Saturday, June 4, 2011

Grails Controllers and REST, part 1

There are plenty of how-to's describing the conventions used by Grails Controllers, so I'm not going into that again.  As I mentioned earlier, Grails Controllers can be a lot more useful than just displaying web pages.  With a little bit of extra plumbing, that same controller that is providing CRUD to HTML pages can be generating JSON or XML to a caller as well.

REST uses the verbs built right into HTTP as the way to define an action to take upon the destination resource, as denoted in the URI.  The Grails conventions fit very nicely into the way humans think of REST, and with a combination of UrlMappings and the "withFormat" command, you can easily extend a Grails Controller to accommodate non-HTML formats.  We'll get into it after the jump.



In part 1, I'm going to focus on HTTP GET.  In part 2, I'll expand out to PUT, DELETE, and POST.

We will start with a simple controller with the standard "list/save/update/delete" methods. I'm not going to put all the code here; you can pull the code down from github to inspect everything. Let's focus first on the list method:

def list = {
    params.max = Math.min(params.max ? params.int('max') : 10, 100)
    [cowInstanceList: Cow.list(params), cowInstanceTotal: Cow.count()]
}

A list is a request for all of the resources of a particular type. We can define our API to say that /rest/cow/list will return a list of Cows. The UrlMapping for that is:

"/rest/$controller/list" {
    action = [GET:"list"]
}

A GET at our URI will be translated to the list method.  Notice we're using the $controller placeholder to capture the "cow" part of the URL. This prepares us for any other REST controllers in our application.  But right now, the list method only returns HTML. We want to support something more machine readable, don't we? With the addition of a small amount of code, we can export JSON and XML.

def list = {
    params.max = Math.min(params.max ? params.int('max') : 10, 100)
    withFormat {
        html { [cowInstanceList: Cow.list(params), cowInstanceTotal: Cow.count()] }
        xml { render Cow.list(params) as XML }
        json { render Cow.list(params) as JSON }
    }
}

Now, this code is assuming that the domain class maps very well to the outside expectations of a Cow. If that's not the case, then you would need to either convert the Cow to a different class or create a custom formatter that would export only parts of the Cow. (We won't go into detail about the parts of a cow, there are plenty of articles about that already.) Perhaps we'll return to custom formatters in a later post.

So, we have quickly created a method that will provide XML or JSON representations of all the Cow instances in our database. Why was it so easy? First, Grails already understands JSON and XML; the creators of Grails knew you'd be wanting it sooner or later. The content type can be specified by the caller either via Accepts=application/json HTTP header, or via an extension to the URI (see below). Either of those things will set the format parameter, which the withFormat method uses to determine which formatting closure to call.

We can try out the new method, right from our browser:
Let's move on to show. The show method returns a single element, not a list. Therefore, we're going to need the ID of the value to be retrieved. Our URL mapping for REST for elements is:

"/rest/$controller/element/$id"{
    action = [GET:"show"]
}

Our original show method looked like this:

def show = {
    def cowInstance = Cow.get(params.id)
    if (!cowInstance) {
        flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'cow.label', default: 'Cow'), params.id])}"
        redirect(action: "list")
    }
    else {
        [cowInstance: cowInstance]
    }
}

Which is, "get the given record; if it doesn't exists, tell the user it was not found, else, pass it along to the view to be rendered." Here's the functionality with the REST-y code sprinkled in.

def show = {
    def cowInstance = Cow.get(params.id)
    if (!cowInstance) {
        withFormat renderNotFound
    }
    else {
        withFormat {
            html { [cowInstance: cowInstance] }
            xml { render cowInstance as XML }
            json { render cowInstance as JSON }
        }
    }
}

Same flow!  Notice the renderNotFound block.  That's a general purpose closure I've put somewhere else, so it can be reused in the other methods.

def renderNotFound = {
    html {
        flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'cow.label', default: 'Cow'), params.id])}"
        redirect(action: "list")
    }
    xml {
        response.status = 404
        render "Cow not found."
    }
    json  {
        response.status = 404
        render "Cow not found."
    }
}

response.status = 404?  Exactly!  Perfect way to tell our mechanical caller the same thing that default.not.found.message is telling the user. After all, doesn't a machine deserve to be treated like a user? (Okay, maybe not...)

These too can be attempted directly from the browser. Assuming there is something saved in your app (like via the HTML create page), then:

And be sure to try accessing an ID that doesn't exist.

So, what I've said not too briefly is that it can be easy to start incorporating REST interfaces into your standard Grails controllers. In most cases, there is no need for different methods, or separate controllers altogether (gasp!). In part 2 of this series, I'll talk about implementing save, update & delete. They are far more interesting, as well as far more verbose.

3 comments: