(Quick Reference)
9 Web Services - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith, Lari Hotari
Version: 2.5.3
9 Web Services
Web Services are all about providing a web API onto your web application and are typically implemented in either
REST or
SOAP
9.1 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.
9.1.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/myapp/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/myapp/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
Config.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/myapp/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/myapp/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/myapp/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/myapp/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.
9.1.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.
9.1.3 Linking to REST resources
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.
9.1.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.v1class BookController {
static namespace = 'v1'
}package myapp.v2class 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/myapp/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
Config.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 org.codehaus.groovy.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 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/myapp/books
9.1.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.
9.1.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 {
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 |
Note that 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
}
}
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.transaction.*
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. If this is not a requirement they can be discarded.
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.transaction.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 representations 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 we 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, expect 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.
9.1.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]
9.1.6 Customizing Response Rendering
There are several ways to customize response rendering in Grails.
9.1.6.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.
9.1.6.2 Registering Custom Objects Marshallers
Grails' Converters feature the notion of an
ObjectMarshaller and each type can have a registered
ObjectMarshaller
. You can register custom
ObjectMarshaller
instances to completely customize response rendering. For example, you can define the following in
BootStrap.init
:
XML.registerObjectMarshaller Book, { Book book, XML xml ->
xml.attribute 'id', book.id
xml.build {
title(book.title)
}
}
You can customize the formatting of an individual value this way too. For example the
JodaTime plugin does the following to support rendering of JodaTime dates in JSON output:
JSON.registerObjectMarshaller(DateTime) {
return it?.toString("yyyy-MM-dd'T'HH:mm:ss'Z'")
}
In the case of JSON it's often simple to use a map to customize output:
JSON.registerObjectMarshaller(Book) {
def map= [:]
map['titl'] = it.title
map['auth'] = it.author
return map
}
Registering Custom Marshallers via Spring
Note that if you have many custom marshallers it is recommended you split the registration of these into a separate class:
class CustomMarshallerRegistrar { @javax.annotation.PostConstruct
void registerMarshallers() {
JSON.registerObjectMarshaller(DateTime) {
return it?.toString("yyyy-MM-dd'T'HH:mm:ss'Z'")
}
}
}
Then define this class as Spring bean in
grails-app/conf/spring/resources.groovy
:
beans = {
myCustomMarshallerRegistrar(CustomMarshallerRegistrar)
}
The
PostConstruct
annotation will get triggered on startup of your application.
9.1.6.3 Using Named Configurations for Object Marshallers
It is also possible to register named configurations. For example:
XML.createNamedConfig('publicApi') {
it.registerObjectMarshaller(Book) { Book book, XML xml ->
// do public API
}
}
XML.createNamedConfig('adminApi') {
it.registerObjectMarshaller(Book) { Book book, XML xml ->
// do admin API
}
}
Then when you use either the
render
or
respond
methods you can wrap the call in a named configuration if necessary to customize rendering per request:
XML.use( isAdmin ? 'adminApi' : 'publicApi') {
render book as XML
}
or
XML.use( isAdmin ? 'adminApi' : 'publicApi') {
respond book
}
9.1.6.4 Implementing the ObjectMarshaller Interface
For more complex marshallers it is recommended you implement the
ObjectMarshaller interface. For example given a domain class:
class Book {
String title
}
By default the output when using:
Would look like:
<book id="1">
<title>The Stand</title>
</book>
To write a custom marshaller you can do the following:
class BookMarshaller implements ObjectMarshaller<XML> { public boolean supports(Object object) {
return object instanceof Book
} public void marshalObject(Object object, XML converter) {
Book book = (Book)object
converter.chars book.title
}
}
And then register the marshaller with:
XML.registerObjectMarshaller(new BookMarshaller())
With the custom
ObjectMarshaller
in place, the output is now:
Customizing the Name of the Root Element
If you wish the customize the name of the surrounding element, you can implement
NameAwareMarshaller:
class BookMarshaller implements ObjectMarshaller<XML>,NameAwareMarshaller { ... String getElementName(Object o) {
return 'custom-book'
}}
With the above change the output would now be:
<custom-book>The Stand</custom-book>
Outputting Markup Using the Converters API or Builder
With the passed Converter object you can explicitly code to the Converters API to stream markup to the response:
public void marshalObject(Object object, XML converter) {
Book book = (Book)object converter.attribute 'id', book.id.toString()
converter.attribute 'date-released', book.dateReleased.toString() converter.startNode 'title'
converter.chars book.title
converter.end()}
The above code results in:
<book id="1" date-released="...">
<title>The Stand</title>
</book>
You can also use a builder notation to achieve a similar result (although the builder notation does not work for
CompileStatic
):
public void marshalObject(Object object, XML converter) {
Book b = (Book)object converter.build {
book(id: b.id) {
title b.title
}
}
}
Using the convertAnother Method to Recursively Convert Objects
To create more complex responses you can use the
convertAnother
method to convert associations and other objects:
public void marshalObject(Object object, XML converter) {
Book book = (Book)object converter.startNode 'title'
converter.chars book.title
converter.end() if (book.authors) {
converter.startNode 'authors'
for(author in book.authors) {
converter.convertAnother author
}
converter.end()
}
}
9.1.6.5 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 org.codehaus.groovy.grails.web.mime.MimeTypeclass 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"/>
Note that 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) {
....
}
}
9.1.6.6 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}"/>
HATEOS, 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.
9.1.7.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)
}
With the bean in place requesting the HAL content type will return HAL:
$ curl -i -H "Accept: application/hal+json" http://localhost:8080/myapp/books/1HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/hal+json;charset=ISO-8859-1{
"_links": {
"self": {
"href": "http://localhost:8080/myapp/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/myapp/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/myapp/books",
"hreflang": "en",
"type": "application/hal+json"
}
},
"_embedded": {
"book": [
{
"_links": {
"self": {
"href": "http://localhost:8080/myapp/books/1",
"hreflang": "en",
"type": "application/hal+json"
}
},
"title": "The Stand"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/myapp/books/2",
"hreflang": "en",
"type": "application/hal+json"
}
},
"title": "Infinite Jest"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/myapp/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/myapp/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/myapp/books",
"hreflang": "en",
"type": "application/hal+json"
}
},
"_embedded": {
"publications": [
{
"_links": {
"self": {
"href": "http://localhost:8080/myapp/books/1",
"hreflang": "en",
"type": "application/hal+json"
}
},
"title": "The Stand"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/myapp/books/2",
"hreflang": "en",
"type": "application/hal+json"
}
},
"title": "Infinite Jest"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/myapp/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/Config.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 org.codehaus.groovy.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
).
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/myapp/books/1HTTP/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/myapp/books/1",
"hreflang": "en",
"type": "application/vnd.books.org.book+json"
}
},
"title": ""The Stand""
}
Customizing Link Rendering
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.link(resource:"publisher", params:[bookId: book.id])
respond book
}
Which will result in output such as:
{
"_links": {
"self": {
"href": "http://localhost:8080/myapp/books/1",
"hreflang": "en",
"type": "application/vnd.books.org.book+json"
}
"publisher": {
"href": "http://localhost:8080/myapp/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.
9.1.7.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)
}
9.1.7.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/myapp/booksHTTP/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/myapp/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"
}
}
}
]
9.1.8 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 // …
}
}
If 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>
<title>The Stand</title>
<authorName>Stephen King</authorName>
</book>
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.codehaus.groovy.grails.web.binding.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.bindingsourceimport org.codehaus.groovy.grails.web.mime.MimeType
import org.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.
// MyCustomDataBindingSourceCreator.groovy in
// src/groovy/com/demo/myapp/databinding
package com.demo.myapp.databindingimport org.codehaus.groovy.grails.web.mime.MimeType
import org.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".
9.2 SOAP
Grails does not feature SOAP support out-of-the-box, but there are several plugins that can help for both producing SOAP servers and calling SOAP web services.
SOAP Clients
To call SOAP web services there are generally 2 approaches taken, one is to use a tool to generate client stubs, the other is to manually construct the SOAP calls. The former can be easier to use, but the latter provides more flexibility / control.
The
CXF client plugin uses the CXF framework, which includes a
wsdl2java
tool for generating a client. There is nothing Groovy/Grails specific here in the generated code as it simply provides a Java API which you can invoke to call SOAP web services.
See the documentation on the
CXF client plugin for further information.
Alternatively, if you prefer more control over your SOAP calls the
WS-Lite library is an excellent choice and features a
Grails plugin. You have more control over the SOAP requests sent, and since Groovy has fantastic support for building and parsing XML it can be very productive approach.
Below is an example of a SOAP call with wslite:
withSoap(serviceURL: 'http://www.holidaywebservice.com/Holidays/US/Dates/USHolidayDates.asmx') {
def response = send {
body {
GetMothersDay(xmlns: 'http://www.27seconds.com/Holidays/US/Dates/') {
year(2011)
}
}
}
println response.GetMothersDayResponse.GetMothersDayResult.text()
}
It is not recommended that you use the
GroovyWS library, it pulls in many dependencies which increases the likelihood of conflicts. The WSlite library provides a far simpler and easier to use solution.
SOAP Servers
Again, Grails does not have direct support for exposing SOAP web services, however if you wish to expose a SOAP service from your application then the
CXF plugin (not to be confused with the cxf-client plugin), provides an easy way to do so.
Typically it involves taking a Grails service and adding 'expose'-style configuration, such as the below:
static expose = EndpointType.JAX_WS_WSDL
//your path (preferred) or url to wsdl
static wsdl = 'org/grails/cxf/test/soap/CustomerService.wsdl'
Please refer to the
documentation of the plugin for more information.
9.3 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. There is however a
Feeds plugin available for Grails that provides a RSS and Atom builder using the popular
ROME library. An example of its usage can be seen below:
def feed() {
render(feedType: "rss", feedVersion: "2.0") {
title = "My test feed"
link = "http://your.test.server/yourController/feed" for (article in Article.list()) {
entry(article.title) {
link = "http://your.test.server/article/${article.id}"
article.content // return the content
}
}
}
}