Grails 2.3 added a lot of support for RESTful services. For example we can now use a respond()
method in our controllers to automatically render resources. The respond()
method accepts a resource instance as argument and a map of attributes that can be passed. We can use the includes
and excludes
keys of the map to pass which fields of our resources need to be included or excluded in the response. This way we can render partial responses based on a request parameter value.
First we start with a simple domain class Book
:
// File: grails-app/domain/com/mrhaki/grails/rest/Book.groovy package com.mrhaki.grails.rest class Book { String title String isbn int numberOfPages }
Next we create a controller BookApiController
, which we extend from the RestfulController
so we already get a lot of basic functionality to render an instance of Book
. We overwrite the index()
and show()
methods, because these are used to display our resource. We use the request parameter fields
to define a comma separated list of fields and pass it to the includes
attribute of the map we use with the respond()
method. Notice we also set the excludes
attribute to remove the class
property from the output.
// File: grails-app/controllers/com/mrhaki/grails/rest/BookApiController.groovy package com.mrhaki.grails.rest import grails.rest.RestfulController class BookApiController extends RestfulController<Book> { // We support both JSON and XML. static responseFormats = ['json', 'xml'] BookApiController() { super(Book) } @Override def show() { // We pass which fields to be rendered with the includes attributes, // we exclude the class property for all responses. respond queryForResource(params.id), [includes: includeFields, excludes: ['class']] } @Override def index(final Integer max) { params.max = Math.min(max ?: 10, 100) respond listAllResources(params), [includes: includeFields, excludes: ['class']] } private getIncludeFields() { params.fields?.tokenize(',') } }
Next we define a mapping to our controller in UrlMappings.groovy
:
// File: grails-app/conf/UrlMappings.groovy class UrlMappings { static mappings = { ... "/api/book"(resources: "bookApi") ... } }
We are now almost done. We only have to register a new Spring component for the collection rendering of our Book
resources. This is necessary to allow the usage of the includes
and excludes
attributes. These attributes are passed to the so-called componentType of the collection. In our case the componentType is the Book
class.
// File: grails-app/conf/spring/resources.groovy import com.mrhaki.grails.rest.Book import grails.rest.render.json.JsonCollectionRenderer import grails.rest.render.xml.XmlCollectionRenderer beans = { // The name of the component is not relevant. // The constructor argument Book sets the componentType for // the collection renderer. jsonBookCollectionRenderer(JsonCollectionRenderer, Book) xmlBookCollectionRenderer(XmlCollectionRenderer, Book) // Uncomment the following to register collection renderers // for all domain classes in the application. // for (domainClass in grailsApplication.domainClasses) { // "json${domainClass.shortName}CollectionRenderer(JsonCollectionRenderer, domainClass.clazz) // "xml${domainClass.shortName}CollectionRenderer(XmlCollectionRenderer, domainClass.clazz) // } }
Now it is time to see our partial responses in action using some simple cURL invocations:
$ curl -X GET -H "Accept:application/json" http://localhost:9000/custom-renderers/api/book?fields=title [ { "title": "It" }, { "title": "The stand" } ] $ curl -X GET -H "Accept:application/xml" http://localhost:9000/custom-renderers/api/book?fields=title,isbn <?xml version="1.0" encoding="UTF-8"?> <list> <book> <isbn> 0451169514 </isbn> <title> It </title> </book> <book> <isbn> 0307743683 </isbn> <title> The stand </title> </book> </list> $ curl -X GET -H "Accept:application/json" http://localhost:9000/custom-renderers/api/book/1 { "id": 1, "isbn": "0451169514", "numberOfPages": 1104, "title": "It" } $ curl -X GET -H "Accept:application/json" http://localhost:9000/custom-renderers/api/book/1?fields=isbn,id { "id": 1, "isbn": "0451169514" }
Code written with Grails 2.3.2