import grails.rest.render.xml.*
beans = {
bookRenderer(XmlRenderer, Book) {
includes = ['title']
}
}
10.10 Customizing Response Rendering
Version: 3.3.8
Table of Contents
10.10 Customizing Response Rendering
If you are looking for a more low-level API and JSON or Markup views don’t suite your needs then you may want to consider implementing a custom renderer.
10.10.1 Customizing the Default Renderers
The default renderers for XML and JSON can be found in the grails.rest.render.xml
and grails.rest.render.json
packages respectively. These use the Grails converters (grails.converters.XML
and grails.converters.JSON
) by default for response rendering.
You can easily customize response rendering using these default renderers. A common change you may want to make is to include or exclude certain properties from rendering.
Including or Excluding Properties from Rendering
As mentioned previously, Grails maintains a registry of grails.rest.render.Renderer
instances. There are some default configured renderers and the ability to register or override renderers for a given domain class or even for a collection of domain classes. To include a particular property from rendering you need to register a custom renderer by defining a bean in grails-app/conf/spring/resources.groovy
:
The bean name is not important (Grails will scan the application context for all registered renderer beans), but for organizational and readability purposes it is recommended you name it something meaningful. |
To exclude a property, the excludes
property of the XmlRenderer
class can be used:
import grails.rest.render.xml.*
beans = {
bookRenderer(XmlRenderer, Book) {
excludes = ['isbn']
}
}
Customizing the Converters
As mentioned previously, the default renders use the grails.converters
package under the covers. In other words, under the covers they essentially do the following:
import grails.converters.*
...
render book as XML
// or render book as JSON
Why the separation between converters and renderers? Well a renderer has more flexibility to use whatever rendering technology you chose. When implementing a custom renderer you could use Jackson, Gson or any Java library to implement the renderer. Converters on the other hand are very much tied to Grails' own marshalling implementation.
10.10.2 Implementing a Custom Renderer
If you want even more control of the rendering or prefer to use your own marshalling techniques then you can implement your own Renderer
instance. For example below is a simple implementation that customizes the rendering of the Book
class:
package myapp
import grails.rest.render.*
import grails.web.mime.MimeType
class BookXmlRenderer extends AbstractRenderer<Book> {
BookXmlRenderer() {
super(Book, [MimeType.XML,MimeType.TEXT_XML] as MimeType[])
}
void render(Book object, RenderContext context) {
context.contentType = MimeType.XML.name
def xml = new groovy.xml.MarkupBuilder(context.writer)
xml.book(id: object.id, title:object.title)
}
}
The AbstractRenderer
super class has a constructor that takes the class that it renders and the MimeType
(s) that are accepted (via the ACCEPT header or file extension) for the renderer.
To configure this renderer, simply add it is a bean to grails-app/conf/spring/resources.groovy
:
beans = {
bookRenderer(myapp.BookXmlRenderer)
}
The result will be that all Book
instances will be rendered in the following format:
<book id="1" title="The Stand"/>
If you change the rendering to a completely different format like the above, then you also need to change the binding if you plan to support POST and PUT requests. Grails will not automatically know how to bind data from a custom XML format to a domain class otherwise. See the section on "Customizing Binding of Resources" for further information. |
Container Renderers
A grails.rest.render.ContainerRenderer
is a renderer that renders responses for containers of objects (lists, maps, collections etc.). The interface is largely the same as the Renderer
interface except for the addition of the getComponentType()
method, which should return the "contained" type. For example:
class BookListRenderer implements ContainerRenderer<List, Book> {
Class<List> getTargetType() { List }
Class<Book> getComponentType() { Book }
MimeType[] getMimeTypes() { [ MimeType.XML] as MimeType[] }
void render(List object, RenderContext context) {
....
}
}
10.10.3 Using GSP to Customize Rendering
You can also customize rendering on a per action basis using Groovy Server Pages (GSP). For example given the show
action mentioned previously:
def show(Book book) {
respond book
}
You could supply a show.xml.gsp
file to customize the rendering of the XML:
<%@page contentType="application/xml"%>
<book id="${book.id}" title="${book.title}"/>