(Quick Reference)

10 REST

Version: 5.0.1

10 REST

REST is not really a technology in itself, but more an architectural pattern. REST is very simple and just involves using plain XML or JSON as a communication medium, combined with URL patterns that are "representational" of the underlying system, and HTTP methods such as GET, PUT, POST and DELETE.

Each HTTP method maps to an action type. For example GET for retrieving data, POST for creating data, PUT for updating and so on.

Grails includes flexible features that make it easy to create RESTful APIs. Creating a RESTful resource can be as simple as one line of code, as demonstrated in the next section.

10.1 Domain classes as REST resources

The easiest way to create a RESTful API in Grails is to expose a domain class as a REST resource. This can be done by adding the grails.rest.Resource transformation to any domain class:

import grails.rest.*

@Resource(uri='/books')
class Book {

    String title

    static constraints = {
        title blank:false
    }
}

Simply by adding the Resource transformation and specifying a URI, your domain class will automatically be available as a REST resource in either XML or JSON formats. The transformation will automatically register the necessary RESTful URL mapping and create a controller called BookController.

You can try it out by adding some test data to BootStrap.groovy:

def init = { servletContext ->
    new Book(title:"The Stand").save()
    new Book(title:"The Shining").save()
}

And then hitting the URL http://localhost:8080/books/1, which will render the response like:

<?xml version="1.0" encoding="UTF-8"?>
<book id="1">
    <title>The Stand</title>
</book>

If you change the URL to http://localhost:8080/books/1.json you will get a JSON response such as:

{"id":1,"title":"The Stand"}

If you wish to change the default to return JSON instead of XML, you can do this by setting the formats attribute of the Resource transformation:

import grails.rest.*

@Resource(uri='/books', formats=['json', 'xml'])
class Book {
    ...
}

With the above example JSON will be prioritized. The list that is passed should contain the names of the formats that the resource should expose. The names of formats are defined in the grails.mime.types setting of application.groovy:

grails.mime.types = [
    ...
    json:          ['application/json', 'text/json'],
    ...
    xml:           ['text/xml', 'application/xml']
]

See the section on Configuring Mime Types in the user guide for more information.

Instead of using the file extension in the URI, you can also obtain a JSON response using the ACCEPT header. Here’s an example using the Unix curl tool:

$ curl -i -H "Accept: application/json" localhost:8080/books/1
{"id":1,"title":"The Stand"}

This works thanks to Grails' Content Negotiation features.

You can create a new resource by issuing a POST request:

$ curl -i -X POST -H "Content-Type: application/json" -d '{"title":"Along Came A Spider"}' localhost:8080/books
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
...

Updating can be done with a PUT request:

$ curl -i -X PUT -H "Content-Type: application/json" -d '{"title":"Along Came A Spider"}' localhost:8080/books/1
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
...

Finally a resource can be deleted with DELETE request:

$ curl -i -X DELETE localhost:8080/books/1
HTTP/1.1 204 No Content
Server: Apache-Coyote/1.1
...

As you can see, the Resource transformation enables all of the HTTP method verbs on the resource. You can enable only read-only capabilities by setting the readOnly attribute to true:

import grails.rest.*

@Resource(uri='/books', readOnly=true)
class Book {
    ...
}

In this case POST, PUT and DELETE requests will be forbidden.

10.2 Mapping to REST resources

If you prefer to keep the declaration of the URL mapping in your UrlMappings.groovy file then simply removing the uri attribute of the Resource transformation and adding the following line to UrlMappings.groovy will suffice:

"/books"(resources:"book")

Extending your API to include more end points then becomes trivial:

"/books"(resources:"book") {
    "/publisher"(controller:"publisher", method:"GET")
}

The above example will expose the URI /books/1/publisher.

A more detailed explanation on creating RESTful URL mappings can be found in the URL Mappings section of the user guide.

10.3 Linking to REST resources from GSP pages

The link tag offers an easy way to link to any domain class resource:

<g:link resource="${book}">My Link</g:link>

However, currently you cannot use g:link to link to the DELETE action and most browsers do not support sending the DELETE method directly.

The best way to accomplish this is to use a form submit:

<form action="/book/2" method="post">
         <input type="hidden" name="_method" value="DELETE"/>
</form>

Grails supports overriding the request method via the hidden _method parameter. This is for browser compatibility purposes. This is useful when using restful resource mappings to create powerful web interfaces. To make a link fire this type of event, perhaps capture all click events for links with a data-method attribute and issue a form submit via JavaScript.

10.4 Versioning REST resources

A common requirement with a REST API is to expose different versions at the same time. There are a few ways this can be achieved in Grails.

Versioning using the URI

A common approach is to use the URI to version APIs (although this approach is discouraged in favour of Hypermedia). For example, you can define the following URL mappings:

"/books/v1"(resources:"book", namespace:'v1')
"/books/v2"(resources:"book", namespace:'v2')

That will match the following controllers:

package myapp.v1

class BookController {
    static namespace = 'v1'
}

package myapp.v2

class BookController {
    static namespace = 'v2'
}

This approach has the disadvantage of requiring two different URI namespaces for your API.

Versioning with the Accept-Version header

As an alternative Grails supports the passing of an Accept-Version header from clients. For example you can define the following URL mappings:

"/books"(version:'1.0', resources:"book", namespace:'v1')
"/books"(version:'2.0', resources:"book", namespace:'v2')

Then in the client simply pass which version you need using the Accept-Version header:

$ curl -i -H "Accept-Version: 1.0" -X GET http://localhost:8080/books

Versioning using Hypermedia / Mime Types

Another approach to versioning is to use Mime Type definitions to declare the version of your custom media types (see the section on "Hypermedia as the Engine of Application State" for more information about Hypermedia concepts). For example, in application.groovy you can declare a custom Mime Type for your resource that includes a version parameter (the 'v' parameter):

grails.mime.types = [
    all: '*/*',
    book: "application/vnd.books.org.book+json;v=1.0",
    bookv2: "application/vnd.books.org.book+json;v=2.0",
    ...
}
It is critical that place your new mime types after the 'all' Mime Type because if the Content Type of the request cannot be established then the first entry in the map is used for the response. If you have your new Mime Type at the top then Grails will always try and send back your new Mime Type if the requested Mime Type cannot be established.

Then override the renderer (see the section on "Customizing Response Rendering" for more information on custom renderers) to send back the custom Mime Type in grails-app/conf/spring/resourses.groovy:

import grails.rest.render.json.*
import grails.web.mime.*

beans = {
    bookRendererV1(JsonRenderer, myapp.v1.Book, new MimeType("application/vnd.books.org.book+json", [v:"1.0"]))
    bookRendererV2(JsonRenderer, myapp.v2.Book, new MimeType("application/vnd.books.org.book+json", [v:"2.0"]))
}

Then update the list of acceptable response formats in your controller:

class BookController extends RestfulController {
    static responseFormats = ['json', 'xml', 'book', 'bookv2']

    // ...
}

Then using the Accept header you can specify which version you need using the Mime Type:

$ curl -i -H "Accept: application/vnd.books.org.book+json;v=1.0" -X GET http://localhost:8080/books

10.5 Implementing REST controllers

The Resource transformation is a quick way to get started, but typically you’ll want to customize the controller logic, the rendering of the response or extend the API to include additional actions.

10.5.1 Extending the RestfulController super class

The easiest way to get started doing so is to create a new controller for your resource that extends the grails.rest.RestfulController super class. For example:

class BookController extends RestfulController<Book> {
    static responseFormats = ['json', 'xml']
    BookController() {
        super(Book)
    }
}

To customize any logic you can just override the appropriate action. The following table provides the names of the action names and the URIs they map to:

HTTP Method URI Controller Action

GET

/books

index

GET

/books/create

create

POST

/books

save

GET

/books/${id}

show

GET

/books/${id}/edit

edit

PUT

/books/${id}

update

DELETE

/books/${id}

delete

The create and edit actions are only needed if the controller exposes an HTML interface.

As an example, if you have a nested resource then you would typically want to query both the parent and the child identifiers. For example, given the following URL mapping:

"/authors"(resources:'author') {
    "/books"(resources:'book')
}

You could implement the nested controller as follows:

class BookController extends RestfulController {
    static responseFormats = ['json', 'xml']
    BookController() {
        super(Book)
    }

    @Override
    protected Book queryForResource(Serializable id) {
        Book.where {
            id == id && author.id == params.authorId
        }.find()
    }

}

The example above subclasses RestfulController and overrides the protected queryForResource method to customize the query for the resource to take into account the parent resource.

Customizing Data Binding In A RestfulController Subclass

The RestfulController class contains code which does data binding for actions like save and update. The class defines a getObjectToBind() method which returns a value which will be used as the source for data binding. For example, the update action does something like this…​

class RestfulController<T> {

    def update() {
        T instance = // retrieve instance from the database...

        instance.properties = getObjectToBind()

        // ...
    }

    // ...
}

By default the getObjectToBind() method returns the request object. When the request object is used as the binding source, if the request has a body then the body will be parsed and its contents will be used to do the data binding, otherwise the request parameters will be used to do the data binding. Subclasses of RestfulController may override the getObjectToBind() method and return anything that is a valid binding source, including a Map or a DataBindingSource. For most use cases binding the request is appropriate but the getObjectToBind() method allows for changing that behavior where desired.

Using custom subclass of RestfulController with Resource annotation

You can also customize the behaviour of the controller that backs the Resource annotation.

The class must provide a constructor that takes a domain class as it’s argument. The second constructor is required for supporting Resource annotation with readOnly=true.

This is a template that can be used for subclassed RestfulController classes used in Resource annotations:

class SubclassRestfulController<T> extends RestfulController<T> {
    SubclassRestfulController(Class<T> domainClass) {
        this(domainClass, false)
    }

    SubclassRestfulController(Class<T> domainClass, boolean readOnly) {
        super(domainClass, readOnly)
    }
}

You can specify the super class of the controller that backs the Resource annotation with the superClass attribute.

import grails.rest.*

@Resource(uri='/books', superClass=SubclassRestfulController)
class Book {

    String title

    static constraints = {
        title blank:false
    }
}

10.5.2 Implementing REST Controllers Step by Step

If you don’t want to take advantage of the features provided by the RestfulController super class, then you can implement each HTTP verb yourself manually. The first step is to create a controller:

$ grails create-controller book

Then add some useful imports and enable readOnly by default:

import grails.gorm.transactions.*
import static org.springframework.http.HttpStatus.*
import static org.springframework.http.HttpMethod.*

@Transactional(readOnly = true)
class BookController {
    ...
}

Recall that each HTTP verb matches a particular Grails action according to the following conventions:

HTTP Method URI Controller Action

GET

/books

index

GET

/books/${id}

show

GET

/books/create

create

GET

/books/${id}/edit

edit

POST

/books

save

PUT

/books/${id}

update

DELETE

/books/${id}

delete

The create and edit actions are already required if you plan to implement an HTML interface for the REST resource. They are there in order to render appropriate HTML forms to create and edit a resource. They can be discarded if that is not a requirement.

The key to implementing REST actions is the respond method introduced in Grails 2.3. The respond method tries to produce the most appropriate response for the requested content type (JSON, XML, HTML etc.)

Implementing the 'index' action

For example, to implement the index action, simply call the respond method passing the list of objects to respond with:

def index(Integer max) {
    params.max = Math.min(max ?: 10, 100)
    respond Book.list(params), model:[bookCount: Book.count()]
}

Note that in the above example we also use the model argument of the respond method to supply the total count. This is only required if you plan to support pagination via some user interface.

The respond method will, using Content Negotiation, attempt to reply with the most appropriate response given the content type requested by the client (via the ACCEPT header or file extension).

If the content type is established to be HTML then a model will be produced such that the action above would be the equivalent of writing:

def index(Integer max) {
    params.max = Math.min(max ?: 10, 100)
    [bookList: Book.list(params), bookCount: Book.count()]
}

By providing an index.gsp file you can render an appropriate view for the given model. If the content type is something other than HTML then the respond method will attempt to lookup an appropriate grails.rest.render.Renderer instance that is capable of rendering the passed object. This is done by inspecting the grails.rest.render.RendererRegistry.

By default there are already renderers configured for JSON and XML, to find out how to register a custom renderer see the section on "Customizing Response Rendering".

Implementing the 'show' action

The show action, which is used to display and individual resource by id, can be implemented in one line of Groovy code (excluding the method signature):

def show(Book book) {
    respond book
}

By specifying the domain instance as a parameter to the action Grails will automatically attempt to lookup the domain instance using the id parameter of the request. If the domain instance doesn’t exist, then null will be passed into the action. The respond method will return a 404 error if null is passed otherwise once again it will attempt to render an appropriate response. If the format is HTML then an appropriate model will produced. The following action is functionally equivalent to the above action:

def show(Book book) {
    if(book == null) {
        render status:404
    }
    else {
        return [book: book]
    }
}

Implementing the 'save' action

The save action creates new resource representations. To start off, simply define an action that accepts a resource as the first argument and mark it as Transactional with the grails.gorm.transactions.Transactional transform:

@Transactional
def save(Book book) {
    ...
}

Then the first thing to do is check whether the resource has any validation errors and if so respond with the errors:

if(book.hasErrors()) {
    respond book.errors, view:'create'
}
else {
    ...
}

In the case of HTML the 'create' view will be rendered again so the user can correct the invalid input. In the case of other formats (JSON, XML etc.), the errors object itself will be rendered in the appropriate format and a status code of 422 (UNPROCESSABLE_ENTITY) returned.

If there are no errors then the resource can be saved and an appropriate response sent:

book.save flush:true
    withFormat {
        html {
            flash.message = message(code: 'default.created.message', args: [message(code: 'book.label', default: 'Book'), book.id])
            redirect book
        }
        '*' { render status: CREATED }
    }

In the case of HTML a redirect is issued to the originating resource and for other formats a status code of 201 (CREATED) is returned.

Implementing the 'update' action

The update action updates an existing resource representation and is largely similar to the save action. First define the method signature:

@Transactional
def update(Book book) {
    ...
}

If the resource exists then Grails will load the resource, otherwise null is passed. In the case of null, you should return a 404:

if(book == null) {
        render status: NOT_FOUND
    }
    else {
        ...
    }

Then once again check for errors validation errors and if so respond with the errors:

if(book.hasErrors()) {
    respond book.errors, view:'edit'
}
else {
    ...
}

In the case of HTML the 'edit' view will be rendered again so the user can correct the invalid input. In the case of other formats (JSON, XML etc.) the errors object itself will be rendered in the appropriate format and a status code of 422 (UNPROCESSABLE_ENTITY) returned.

If there are no errors then the resource can be saved and an appropriate response sent:

book.save flush:true
withFormat {
    html {
        flash.message = message(code: 'default.updated.message', args: [message(code: 'book.label', default: 'Book'), book.id])
        redirect book
    }
    '*' { render status: OK }
}

In the case of HTML a redirect is issued to the originating resource and for other formats a status code of 200 (OK) is returned.

Implementing the 'delete' action

The delete action deletes an existing resource. The implementation is largely similar to the update action, except the delete() method is called instead:

book.delete flush:true
withFormat {
    html {
        flash.message = message(code: 'default.deleted.message', args: [message(code: 'Book.label', default: 'Book'), book.id])
        redirect action:"index", method:"GET"
    }
    '*'{ render status: NO_CONTENT }
}

Notice that for an HTML response a redirect is issued back to the index action, whilst for other content types a response code 204 (NO_CONTENT) is returned.

10.5.3 Generating a REST controller using scaffolding

To see some of these concepts in action and help you get going, the Scaffolding plugin, version 2.0 and above, can generate a REST ready controller for you, simply run the command:

$ grails generate-controller <<Domain Class Name>>

10.6 Calling REST Services with HttpClient

Calling Grails REST services - as well as third-party services - is very straightforward using the Micronaut HTTP Client. This HTTP client has both a low-level API and a higher level AOP-driven API, making it useful for both simple requests as well as building declarative, type-safe API layers.

To use the Micronaut HTTP client you must have the micronaut-http-client dependency on your classpath. Add the following dependency to your build.gradle file.

build.gradle
compile 'io.micronaut:micronaut-http-client'

Low-level API

The HttpClient interface forms the basis for the low-level API. This interfaces declares methods to help ease executing HTTP requests and receive responses.

The majority of the methods in the HttpClient interface returns Reactive Streams Publisher instances, and a sub-interface called RxHttpClient is included that provides a variation of the HttpClient interface that returns RxJava Flowable types. When using HttpClient in a blocking flow, you may wish to call toBlocking() to return an instance of BlockingHttpClient.

There are a few ways by which you can obtain a reference to a HttpClient. The most simple way is using the create method

Creating an HTTP client
    List<Album> searchWithApi(String searchTerm) {
        String baseUrl = "https://itunes.apple.com/"

        BlockingHttpClient client = HttpClient.create(baseUrl.toURL()).toBlocking() (1)

        HttpRequest request = HttpRequest.GET("/search?limit=25&media=music&entity=album&term=${searchTerm}")
        HttpResponse<String> resp = client.exchange(request, String)
        client.close() (2)

        String json = resp.body()
        ObjectMapper objectMapper = new ObjectMapper() (3)
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        SearchResult searchResult = objectMapper.readValue(json, SearchResult)
        searchResult.results
    }
1 Create a new instance of HttpClient with the create method, and convert to an instance of BlockingHttpClient with toBlocking(),
2 The client should be closed using the close method to prevent thread leaking.
3 Jackson’s ObjectMapper API can be used to map the raw JSON to POGOs, in this case SearchResult

Consult the Http Client section of the Micronaut user guide for more information on using the HttpClient low-level API.

Declarative API

A declarative HTTP client can be written by adding the @Client annotation to any interface or abstract class. Using Micronaut’s AOP support (see the Micronaut user guide section on Introduction Advice), the abstract or interface methods will be implemented for you at compilation time as HTTP calls. Declarative clients can return data-bound POGOs (or POJOs) without requiring special handling from the calling code.

package example.grails

import io.micronaut.http.annotation.Get
import io.micronaut.http.client.annotation.Client


@Client("https://start.grails.org")
interface GrailsAppForgeClient {

    @Get("/{version}/profiles")
    List<Map> profiles(String version)
}

Note that HTTP client methods are annotated with the appropriate HTTP method, such as @Get or @Post.

To use a client like the one in the above example, simply inject an instance of the client into any bean using the @Autowired annotation.

  @Autowired GrailsAppForgeClient appForgeClient

    List<Map> profiles(String grailsVersion) {
        respond appForgeClient.profiles(grailsVersion)
    }

For more details on writing and using declarative clients, consult the Http Client section of the Micronaut user guide.

10.7 The REST Profile

Since Grails 3.1, Grails supports a tailored profile for creating REST applications that provides a more focused set of dependencies and commands.

To get started with the REST profile, create an application specifying rest-api as the name of the profile:

$ grails create-app my-api --profile rest-api

This will create a new REST application that provides the following features:

  • Default set of commands for creating and generating REST endpoints

  • Defaults to using JSON views for rendering responses (see the next section)

  • Fewer plugins than the default Grails plugin (no GSP, no Asset Pipeline, nothing HTML related)

You will notice for example in the grails-app/views directory that there are *.gson files for rendering the default index page and as well as any 404 and 500 errors.

If you issue the following set of commands:

$ grails create-domain-class my.api.Book
$ grails generate-all my.api.Book

Instead of CRUD HTML interface a REST endpoint is generated that produces JSON responses. In addition, the generated functional and unit tests by default test the REST endpoint.

10.8 The AngularJS Profile

Since Grails 3.1, Grails supports a profile for creating applications with AngularJS that provides a more focused set of dependencies and commands. The angular profile inherits from the REST profile and therefore has all of the commands and properties that the REST profile has.

To get started with the AngularJS profile, create an application specifying angularjs as the name of the profile:

$ grails create-app my-api --profile angularjs

This will create a new Grails application that provides the following features:

  • Default set of commands for creating AngularJS artefacts

  • Gradle plugin to manage client side dependencies

  • Gradle plugin to execute client side unit tests

  • Asset Pipeline plugins to ease development

By default the AngularJS profile includes GSP support in order to render the index page. This is necessary because the profile is designed around asset pipeline.

The new commands are:

  • create-ng-component

  • create-ng-controller

  • create-ng-directive

  • create-ng-domain

  • create-ng-module

  • create-ng-service

Project structure

The AngularJS profile is designed around a specific project structure. The create-ng commands will automatically create modules where they do not exist.

Example:

$ grails create-ng-controller foo

This will produce a fooController.js file in grails-app/assets/javascripts/${default package name}/controllers.

By default the angularjs profile will create files in the javascripts directory. You can change that behavior in your configuration with the key grails.codegen.angular.assetDir.
$ grails create-ng-domain foo.bar

This will produce a Bar.js file in grails-app/assets/javascripts/foo/domains. It will also create the "foo" module if it does not already exist.

$ grails create-ng-module foo.bar

This will produce a foo.bar.js file in grails-app/assets/javascripts/foo/bar. Note the naming convention for modules is different than other artefacts.

$ grails create-ng-service foo.bar --type constant

This will produce a bar.js file in grails-app/assets/javascripts/foo/services. It will also create the "foo" module if it does not already exist. The create-ng-service command accepts a flag -type. The types that can be used are:

  • service

  • factory default

  • value

  • provider

  • constant

Along with the artefacts themselves, the profile will also produce a skeleton unit test file under src/test/javascripts for each create command.

Client side dependencies

The Gradle Bower Plugin is used to manage dependencies with bower. Visit the plugin documentation to learn how to use the plugin.

Unit Testing

The Gradle Karma Plugin is used to execute client side unit tests. All generated tests are written with Jasmine. Visit the plugin documentation to learn how to use the plugin.

Asset Pipeline

The AngularJS profile includes several asset pipeline plugins to make development easier.

10.9 The Angular Profile

Since Grails 3.2.1, Grails supports a profile for creating applications with Angular that provides a more future facing setup.

The biggest change in this profile is that the profile creates a multi project gradle build. This is the first profile to have done so. The Angular profile relies on the Angular CLI to manage the client side application. The server side application is the same as an application created with the rest-api profile.

To get started with the Angular profile, create an application specifying angular as the name of the profile:

$ grails create-app my-app --profile angular

This will create a my-app directory with the following contents:

client/
gradle/
gradlew
gradlew.bat
server/
settings.gradle

The entire client application lives in the client folder and the entire server application lives in the server folder.

Prerequisites

To use this profile, you should have Node, NPM, and the Angular CLI installed. Node should be at least version 5 and NPM should be at least version 3.

Project Structure

The Angular profile is designed to be used with the Angular CLI. The CLI was used to create the client application side of the profile to start with. The CLI provides commands to do most of the things you would want to do with the client application, including creating components or services. Because of that, the profile itself provides no commands to do those same things.

Running The App

To execute the server side application only, you can execute the bootRun task in the server project:

./gradlew server:bootRun

The same can be done for the client application:

./gradlew client:bootRun

To execute both, you must do so in parallel:

./gradlew bootRun --parallel
It is necessary to do so in parallel because by default Gradle executes tasks synchronously, and neither of the bootRun tasks will "finish".

Testing

The default client application that comes with the profile provides some tests that can be executed. To execute tests in the application:

./gradlew test

The test task will execute unit tests with Karma and Jasmine.

./gradlew integrationTest

The integrationTest task will execute e2e tests with Protractor.

You can execute the test and integrationTest tasks on each of the sub-projects the same as you would bootRun.

CORS

Because the client side and server side will be running on separate ports, CORS configuration is required. By default the profile will configure the server side to allow CORS from all hosts via the following config:

server/grails-app/conf/application.yml
grails:
    cors:
        enabled: true

See the section on CORS in the user guide for information on configuring this feature for your needs.

10.10 JSON Views

As mentioned in the previous section the REST profile by default uses JSON views to render JSON responses. These play a similar role to GSP, but instead are optimized for outputing JSON responses instead of HTML.

You can continue to separate your application in terms of MVC, with the logic of your application residing in controllers and services, whilst view related matters are handled by JSON views.

JSON views also provide the flexibility to easily customize the JSON presented to clients without having to resort to relatively complex marshalling libraries like Jackson or Grails' marshaller API.

Since Grails 3.1, JSON views are considered by the Grails team the best way to present JSON output for the client, the section on writing custom marshallers has been removed from the user guide. If you are looking for information on that topic, see the Grails 3.0.x guide.

10.10.1 Getting Started

If you are using the REST or AngularJS profiles then the JSON views plugin will already be included and you can skip the remainder of this section. Otherwise you will need to modify your build.gradle to include the necessary plugin to activate JSON views:

compile 'org.grails.plugins:views-json:1.0.0' // or whatever is the latest version
The source code repository for JSON views can be found on Github if you are looking for more documentation and contributions

In order to compile JSON views for production deployment you should also activate the Gradle plugin by first modifying the buildscript block:

buildscript {
    ...
    dependencies {
        ...
        classpath "org.grails.plugins:views-gradle:1.0.0"
    }
}

Then apply the org.grails.plugins.views-json Gradle plugin after any Grails core gradle plugins:

...
apply plugin: "org.grails.grails-web"
apply plugin: "org.grails.plugins.views-json"

This will add a compileGsonViews task to Gradle, which is invoked prior to creating the production JAR or WAR file.

10.10.2 Creating JSON Views

JSON views go into the grails-app/views directory and end with the .gson suffix. They are regular Groovy scripts and can be opened in any Groovy editor.

Example JSON view:

json.person {
    name "bob"
}
To open them in the Groovy editor in Intellij IDEA, double click on the file and when asked which file to associate it with, choose "Groovy"

The above JSON view produces:

{"person":{"name":"bob"}}

There is an implicit json variable which is an instance of StreamingJsonBuilder.

Example usages:

json(1,2,3) == "[1,2,3]"
json { name "Bob" } == '{"name":"Bob"}'
json([1,2,3]) { n it } == '[{"n":1},{"n":2},{"n":3}]'

Refer to the API documentation on StreamingJsonBuilder for more information about what is possible.

10.10.3 JSON View Templates

You can define templates starting with underscore _. For example given the following template called _person.gson:

model {
    Person person
}
json {
    name person.name
    age person.age
}

You can render it with a view as follows:

model {
    Family family
}
json {
    name family.father.name
    age family.father.age
    oldestChild g.render(template:"person", model:[person: family.children.max { Person p -> p.age } ])
    children g.render(template:"person", collection: family.children, var:'person')
}

Alternatively for a more concise way to invoke templates, using the tmpl variable:

model {
    Family family
}
json {
    name family.father.name
    age family.father.age
    oldestChild tmpl.person( family.children.max { Person p -> p.age } ] )
    children tmpl.person( family.children )
}

10.10.4 Rendering Domain Classes with JSON Views

Typically your model may involve one or many domain instances. JSON views provide a render method for rendering these.

For example given the following domain class:

class Book {
    String title
}

And the following template:

model {
    Book book
}

json g.render(book)

The resulting output is:

{id:1, title:"The Stand"}

You can customize the rendering by including or excluding properties:

json g.render(book, [includes:['title']])

Or by providing a closure to add additional JSON output:

json g.render(book) {
    pages 1000
}

10.10.5 JSON Views by Convention

There are a few useful conventions you can follow when creating JSON views. For example if you have a domain class called Book, then creating a template located at grails-app/views/book/_book.gson and using the respond method will result in rendering the template:

def show(Long id) {
    respond Book.get(id)
}

In addition if an error occurs during validation by default Grails will try to render a template called grails-app/views/book/_errors.gson, otherwise it will try to render grails-app/views/errors/_errors.gson if the former doesn’t exist.

This is useful because when persisting objects you can respond with validation errors to render these aforementioned templates:

@Transactional
def save(Book book) {
    if (book.hasErrors()) {
        transactionStatus.setRollbackOnly()
        respond book.errors
    }
    else {
        // valid object
    }
}

If a validation error occurs in the above example the grails-app/views/book/_errors.gson template will be rendered.

For more information on JSON views (and Markup views), see the JSON Views user guide.

10.11 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.11.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:

import grails.rest.render.xml.*

beans = {
    bookRenderer(XmlRenderer, Book) {
        includes = ['title']
    }
}
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.11.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.11.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}"/>

10.12 Hypermedia as the Engine of Application State

HATEOAS, an abbreviation for Hypermedia as the Engine of Application State, is a common pattern applied to REST architectures that uses hypermedia and linking to define the REST API.

Hypermedia (also called Mime or Media Types) are used to describe the state of a REST resource, and links tell clients how to transition to the next state. The format of the response is typically JSON or XML, although standard formats such as Atom and/or HAL are frequently used.

10.12.1 HAL Support

HAL is a standard exchange format commonly used when developing REST APIs that follow HATEOAS principals. An example HAL document representing a list of orders can be seen below:

{
    "_links": {
        "self": { "href": "/orders" },
        "next": { "href": "/orders?page=2" },
        "find": {
            "href": "/orders{?id}",
            "templated": true
        },
        "admin": [{
            "href": "/admins/2",
            "title": "Fred"
        }, {
            "href": "/admins/5",
            "title": "Kate"
        }]
    },
    "currentlyProcessing": 14,
    "shippedToday": 20,
    "_embedded": {
        "order": [{
            "_links": {
                "self": { "href": "/orders/123" },
                "basket": { "href": "/baskets/98712" },
                "customer": { "href": "/customers/7809" }
            },
            "total": 30.00,
            "currency": "USD",
            "status": "shipped"
        }, {
            "_links": {
                "self": { "href": "/orders/124" },
                "basket": { "href": "/baskets/97213" },
                "customer": { "href": "/customers/12369" }
            },
            "total": 20.00,
            "currency": "USD",
            "status": "processing"
        }]
    }
}

Exposing Resources Using HAL

To return HAL instead of regular JSON for a resource you can simply override the renderer in grails-app/conf/spring/resources.groovy with an instance of grails.rest.render.hal.HalJsonRenderer (or HalXmlRenderer for the XML variation):

import grails.rest.render.hal.*
beans = {
    halBookRenderer(HalJsonRenderer, rest.test.Book)
}

You will also need to update the acceptable response formats for the resource so that the HAL format is included. Not doing so will result in a 406 - Not Acceptable response being returned from the server.

This can be done by setting the formats attribute of the Resource transformation:

import grails.rest.*

@Resource(uri='/books', formats=['json', 'xml', 'hal'])
class Book {
    ...
}

Or by updating the responseFormats in the controller:

class BookController extends RestfulController {
    static responseFormats = ['json', 'xml', 'hal']

    // ...
}

With the bean in place requesting the HAL content type will return HAL:

$ curl -i -H "Accept: application/hal+json" http://localhost:8080/books/1

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/hal+json;charset=ISO-8859-1

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/books/1",
      "hreflang": "en",
      "type": "application/hal+json"
    }
  },
  "title": "\"The Stand\""
}

To use HAL XML format simply change the renderer:

import grails.rest.render.hal.*
beans = {
    halBookRenderer(HalXmlRenderer, rest.test.Book)
}

Rendering Collections Using HAL

To return HAL instead of regular JSON for a list of resources you can simply override the renderer in grails-app/conf/spring/resources.groovy with an instance of grails.rest.render.hal.HalJsonCollectionRenderer:

import grails.rest.render.hal.*
beans = {
    halBookCollectionRenderer(HalJsonCollectionRenderer, rest.test.Book)
}

With the bean in place requesting the HAL content type will return HAL:

$ curl -i -H "Accept: application/hal+json" http://localhost:8080/books
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 17 Oct 2013 02:34:14 GMT

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/books",
      "hreflang": "en",
      "type": "application/hal+json"
    }
  },
  "_embedded": {
    "book": [
      {
        "_links": {
          "self": {
            "href": "http://localhost:8080/books/1",
            "hreflang": "en",
            "type": "application/hal+json"
          }
        },
        "title": "The Stand"
      },
      {
        "_links": {
          "self": {
            "href": "http://localhost:8080/books/2",
            "hreflang": "en",
            "type": "application/hal+json"
          }
        },
        "title": "Infinite Jest"
      },
      {
        "_links": {
          "self": {
            "href": "http://localhost:8080/books/3",
            "hreflang": "en",
            "type": "application/hal+json"
          }
        },
        "title": "Walden"
      }
    ]
  }
}

Notice that the key associated with the list of Book objects in the rendered JSON is book which is derived from the type of objects in the collection, namely Book. In order to customize the value of this key assign a value to the collectionName property on the HalJsonCollectionRenderer bean as shown below:

import grails.rest.render.hal.*
beans = {
    halBookCollectionRenderer(HalCollectionJsonRenderer, rest.test.Book) {
        collectionName = 'publications'
    }
}

With that in place the rendered HAL will look like the following:

$ curl -i -H "Accept: application/hal+json" http://localhost:8080/books
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 17 Oct 2013 02:34:14 GMT

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/books",
      "hreflang": "en",
      "type": "application/hal+json"
    }
  },
  "_embedded": {
    "publications": [
      {
        "_links": {
          "self": {
            "href": "http://localhost:8080/books/1",
            "hreflang": "en",
            "type": "application/hal+json"
          }
        },
        "title": "The Stand"
      },
      {
        "_links": {
          "self": {
            "href": "http://localhost:8080/books/2",
            "hreflang": "en",
            "type": "application/hal+json"
          }
        },
        "title": "Infinite Jest"
      },
      {
        "_links": {
          "self": {
            "href": "http://localhost:8080/books/3",
            "hreflang": "en",
            "type": "application/hal+json"
          }
        },
        "title": "Walden"
      }
    ]
  }
}

Using Custom Media / Mime Types

If you wish to use a custom Mime Type then you first need to declare the Mime Types in grails-app/conf/application.groovy:

grails.mime.types = [
    all:      "*/*",
    book:     "application/vnd.books.org.book+json",
    bookList: "application/vnd.books.org.booklist+json",
    ...
]
It is critical that place your new mime types after the 'all' Mime Type because if the Content Type of the request cannot be established then the first entry in the map is used for the response. If you have your new Mime Type at the top then Grails will always try and send back your new Mime Type if the requested Mime Type cannot be established.

Then override the renderer to return HAL using the custom Mime Types:

import grails.rest.render.hal.*
import grails.web.mime.*

beans = {
    halBookRenderer(HalJsonRenderer, rest.test.Book, new MimeType("application/vnd.books.org.book+json", [v:"1.0"]))
    halBookListRenderer(HalJsonCollectionRenderer, rest.test.Book, new MimeType("application/vnd.books.org.booklist+json", [v:"1.0"]))
}

In the above example the first bean defines a HAL renderer for a single book instance that returns a Mime Type of application/vnd.books.org.book+json. The second bean defines the Mime Type used to render a collection of books (in this case application/vnd.books.org.booklist+json).

application/vnd.books.org.booklist+json is an example of a media-range (http://www.w3.org/Protocols/rfc2616/rfc2616.html - Header Field Definitions). This example uses entity (book) and operation (list) to form the media-range values but in reality, it may not be necessary to create a separate Mime type for each operation. Further, it may not be necessary to create Mime types at the entity level. See the section on "Versioning REST resources" for further information about how to define your own Mime types.

With this in place issuing a request for the new Mime Type returns the necessary HAL:

$ curl -i -H "Accept: application/vnd.books.org.book+json" http://localhost:8080/books/1

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/vnd.books.org.book+json;charset=ISO-8859-1

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/books/1",
      "hreflang": "en",
      "type": "application/vnd.books.org.book+json"
    }
  },
  "title": "\"The Stand\""
}

An important aspect of HATEOAS is the usage of links that describe the transitions the client can use to interact with the REST API. By default the HalJsonRenderer will automatically create links for you for associations and to the resource itself (using the "self" relationship).

However you can customize link rendering using the link method that is added to all domain classes annotated with grails.rest.Resource or any class annotated with grails.rest.Linkable. For example, the show action can be modified as follows to provide a new link in the resulting output:

def show(Book book) {
    book.link rel:'publisher', href: g.createLink(absolute: true, resource:"publisher", params:[bookId: book.id])
    respond book
}

Which will result in output such as:

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/books/1",
      "hreflang": "en",
      "type": "application/vnd.books.org.book+json"
    }
    "publisher": {
        "href": "http://localhost:8080/books/1/publisher",
        "hreflang": "en"
    }
  },
  "title": "\"The Stand\""
}

The link method can be passed named arguments that match the properties of the grails.rest.Link class.

10.12.2 Atom Support

Atom is another standard interchange format used to implement REST APIs. An example of Atom output can be seen below:

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Example Feed</title>
 <link href="http://example.org/"/>
 <updated>2003-12-13T18:30:02Z</updated>
 <author>
   <name>John Doe</name>
 </author>
 <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>

 <entry>
   <title>Atom-Powered Robots Run Amok</title>
   <link href="http://example.org/2003/12/13/atom03"/>
   <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
   <updated>2003-12-13T18:30:02Z</updated>
   <summary>Some text.</summary>
 </entry>

</feed>

To use Atom rendering again simply define a custom renderer:

import grails.rest.render.atom.*
beans = {
    halBookRenderer(AtomRenderer, rest.test.Book)
    halBookListRenderer(AtomCollectionRenderer, rest.test.Book)
}

10.12.3 Vnd.Error Support

Vnd.Error is a standardised way of expressing an error response.

By default when a validation error occurs when attempting to POST new resources then the errors object will be sent back allow with a 422 respond code:

$ curl -i -H "Accept: application/json"  -H "Content-Type: application/json" -X POST -d "" http://localhost:8080/books

HTTP/1.1 422 Unprocessable Entity
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=ISO-8859-1

{
  "errors": [
    {
      "object": "rest.test.Book",
      "field": "title",
      "rejected-value": null,
      "message": "Property [title] of class [class rest.test.Book] cannot be null"
    }
  ]
}

If you wish to change the format to Vnd.Error then simply register grails.rest.render.errors.VndErrorJsonRenderer bean in grails-app/conf/spring/resources.groovy:

beans = {
    vndJsonErrorRenderer(grails.rest.render.errors.VndErrorJsonRenderer)
    // for Vnd.Error XML format
    vndXmlErrorRenderer(grails.rest.render.errors.VndErrorXmlRenderer)
}

Then if you alter the client request to accept Vnd.Error you get an appropriate response:

$ curl -i -H "Accept: application/vnd.error+json,application/json" -H "Content-Type: application/json" -X POST -d "" http://localhost:8080/books
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/vnd.error+json;charset=ISO-8859-1

[
    {
        "logref": "book.nullable,
        "message": "Property [title] of class [class rest.test.Book] cannot be null",
        "_links": {
            "resource": {
                "href": "http://localhost:8080/rest-test/books"
            }
        }
    }
]

10.13 Customizing Binding of Resources

The framework provides a sophisticated but simple mechanism for binding REST requests to domain objects and command objects. One way to take advantage of this is to bind the request property in a controller the properties of a domain class. Given the following XML as the body of the request, the createBook action will create a new Book and assign "The Stand" to the title property and "Stephen King" to the authorName property.

<?xml version="1.0" encoding="UTF-8"?>
<book>
    <title>The Stand</title>
    <authorName>Stephen King</authorName>
</book>
class BookController {

    def createBook() {
        def book = new Book()
        book.properties = request

        // ...
    }
}

Command objects will automatically be bound with the body of the request:

class BookController {
    def createBook(BookCommand book) {

        // ...
    }
}

class BookCommand {
    String title
    String authorName
}

If the command object type is a domain class and the root element of the XML document contains an id attribute, the id value will be used to retrieve the corresponding persistent instance from the database and then the rest of the document will be bound to the instance. If no corresponding record is found in the database, the command object reference will be null.

<?xml version="1.0" encoding="UTF-8"?>
<book id="42">
    <title>Walden</title>
    <authorName>Henry David Thoreau</authorName>
</book>
class BookController {
    def updateBook(Book book) {
        // The book will have been retrieved from the database and updated
        // by doing something like this:
        //
        // book == Book.get('42')
        // if(book != null) {
        //    book.properties = request
        // }
        //
        // the code above represents what the framework will
        // have done. There is no need to write that code.

        // ...

    }
}

The data binding depends on an instance of the DataBindingSource interface created by an instance of the DataBindingSourceCreator interface. The specific implementation of DataBindingSourceCreator will be selected based on the contentType of the request. Several implementations are provided to handle common content types. The default implementations will be fine for most use cases. The following table lists the content types which are supported by the core framework and which DataBindingSourceCreator implementations are used for each. All of the implementation classes are in the org.grails.databinding.bindingsource package.

Content Type(s) Bean Name DataBindingSourceCreator Impl.

application/xml, text/xml

xmlDataBindingSourceCreator

XmlDataBindingSourceCreator

application/json, text/json

jsonDataBindingSourceCreator

JsonDataBindingSourceCreator

application/hal+json

halJsonDataBindingSourceCreator

HalJsonDataBindingSourceCreator

application/hal+xml

halXmlDataBindingSourceCreator

HalXmlDataBindingSourceCreator

In order to provide your own DataBindingSourceCreator for any of those content types, write a class which implements DataBindingSourceCreator and register an instance of that class in the Spring application context. If you are replacing one of the existing helpers, use the corresponding bean name from above. If you are providing a helper for a content type other than those accounted for by the core framework, the bean name may be anything that you like but you should take care not to conflict with one of the bean names above.

The DataBindingSourceCreator interface defines just 2 methods:

package org.grails.databinding.bindingsource

import grails.web.mime.MimeType
import grails.databinding.DataBindingSource

/**
 * A factory for DataBindingSource instances
 *
 * @since 2.3
 * @see DataBindingSourceRegistry
 * @see DataBindingSource
 *
 */
interface DataBindingSourceCreator {

    /**
     * `return All of the {`link MimeType} supported by this helper
     */
    MimeType[] getMimeTypes()

    /**
     * Creates a DataBindingSource suitable for binding bindingSource to bindingTarget
     *
     * @param mimeType a mime type
     * @param bindingTarget the target of the data binding
     * @param bindingSource the value being bound
     * @return a DataBindingSource
     */
    DataBindingSource createDataBindingSource(MimeType mimeType, Object bindingTarget, Object bindingSource)
}

AbstractRequestBodyDataBindingSourceCreator is an abstract class designed to be extended to simplify writing custom DataBindingSourceCreator classes. Classes which extend AbstractRequestbodyDatabindingSourceCreator need to implement a method named createBindingSource which accepts an InputStream as an argument and returns a DataBindingSource as well as implementing the getMimeTypes method described in the DataBindingSourceCreator interface above. The InputStream argument to createBindingSource provides access to the body of the request.

The code below shows a simple implementation.

src/main/groovy/com/demo/myapp/databinding/MyCustomDataBindingSourceCreator.groovy
package com.demo.myapp.databinding

import grails.web.mime.MimeType
import grails.databinding.DataBindingSource
import org...databinding.SimpleMapDataBindingSource
import org...databinding.bindingsource.AbstractRequestBodyDataBindingSourceCreator

/**
 * A custom DataBindingSourceCreator capable of parsing key value pairs out of
 * a request body containing a comma separated list of key:value pairs like:
 *
 * name:Herman,age:99,town:STL
 *
 */
class MyCustomDataBindingSourceCreator extends AbstractRequestBodyDataBindingSourceCreator {

    @Override
    public MimeType[] getMimeTypes() {
        [new MimeType('text/custom+demo+csv')] as MimeType[]
    }

    @Override
    protected DataBindingSource createBindingSource(InputStream inputStream) {
        def map = [:]

        def reader = new InputStreamReader(inputStream)

        // this is an obviously naive parser and is intended
        // for demonstration purposes only.

        reader.eachLine { line ->
            def keyValuePairs = line.split(',')
            keyValuePairs.each { keyValuePair ->
                if(keyValuePair?.trim()) {
                    def keyValuePieces = keyValuePair.split(':')
                    def key = keyValuePieces[0].trim()
                    def value = keyValuePieces[1].trim()
                    map<<key>> = value
                }
            }
        }

        // create and return a DataBindingSource which contains the parsed data
        new SimpleMapDataBindingSource(map)
    }
}

An instance of MyCustomDataSourceCreator needs to be registered in the spring application context.

grails-app/conf/spring/resources.groovy
beans = {

    myCustomCreator com.demo.myapp.databinding.MyCustomDataBindingSourceCreator

    // ...
}

With that in place the framework will use the myCustomCreator bean any time a DataBindingSourceCreator is needed to deal with a request which has a contentType of "text/custom+demo+csv".

10.14 RSS and Atom

No direct support is provided for RSS or Atom within Grails. You could construct RSS or ATOM feeds with the render method’s XML capability.