9 Advanced Features
Version: 3.1.0
Table of Contents
9 Advanced Features
9.1 Generate WADL
Automatically generated WADL functionality is only available in version 0.4 of the jaxrs plugin or higher, and requires the Jersey implementation.A WADL document for resources managed by the plugin can be generated by sending a GET request to
/application.wadl.
The result should look like:<application xmlns="http://research.sun.com/wadl/2006/10"> <doc xmlns:jersey="http://jersey.dev.java.net/" jersey:generatedBy="Jersey: 1.1.4.1 11/24/2009 01:30 AM"/> <resources base="http://localhost:8080/"> <resource path="/api/test"> <method name="GET" id="getTestRepresentation"> <request> <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="name"/> </request> <response> <representation mediaType="text/plain"/> </response> </method> </resource> </resources> </application>
9.2 Scaffolding Domains
The jaxrs plugin also supports scaffolding. It allows you to generate a RESTful service interface for one or more domain classes based on JAX-RS resource classes. Supported representation formats are XML and JSON. The following sections walk through a simple example.Create a domain class
To create aPerson domain class go to the project's root directory and enter:grails create-domain-class person
Person.groovy file (under grails-app/domain) and add two properties, firstName and lastName.package helloclass Person { static constraints = { } String firstName String lastName}
Generate the REST API
To generate JAX-RS resources that implement the RESTful service interface for that domain class enter:grails generate-resources hello.Person
PersonCollectionResource.groovy and PersonResource.groovy
(in the hello package) that support HTTP POST, GET, PUT and DELETE operations for creating, reading, updating and
deleting Person objects, respectively. PersonCollectionResource.groovy is related to Person lists,
PersonResource.groovy is related to individual Person instances. Let's take a look at how to use the generated
RESTful service interface.Use the REST API
Start the hello application with:grails run-app
http://localhost:8080/api/person. The following request POSTs an XML
representation of a person object.POST /api/person HTTP/1.1
Content-Type: application/xml
Accept: application/xml
Host: localhost:8080
Content-Length: 82<person>
<firstName>Sam</firstName>
<lastName>Hill</lastName>
</person>application/xml. After sending the request, the server creates a new
person object in the database and returns an XML representation of it.HTTP/1.1 201 Created Content-Type: application/xml Location: http://localhost:8080/api/person/1 Transfer-Encoding: chunked Server: Jetty(6.1.14)<?xml version="1.0" encoding="UTF-8"?> <person id="1"> <firstName>Sam</firstName> <lastName>Hill</lastName> </person>
Accept request header. Note that the returned
representation differs from the submitted representation by an id attribute in the <person> element. This id is
also contained in the Location response header, the URL of the created resource. The response code is 201 (CREATED).
Let's create another person object using a JSON representation. Here's the request:POST /api/person HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: localhost:8080
Content-Length: 58{"class":"Person","firstName":"Fabien","lastName":"Barel"}Accept request header). The id of the
created person object is 2.HTTP/1.1 201 Created
Content-Type: application/json
Location: http://localhost:8080/api/person/2
Transfer-Encoding: chunked
Server: Jetty(6.1.14){"class":"Person","id":"2","firstName":"Fabien","lastName":"Barel"}Content-Type and Accept headers works for other HTTP methods as well. To GET a list of
created persons, open a browser (Firefox in our example) and enter the URL http://localhost:8080/api/person.
This returns an XML representation of the list of persons stored in the database.<list>
<person id="1">
<firstName>Sam</firstName>
<lastName>Hill</lastName>
</person>
<person id="2">
<firstName>Fabien></firstName>
<lastName>Barel</lastName>
</person>
</list>Accept=text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
request header. To get the representation of a single person, specify the id in the URL. For example, to get the
person with id 1 use http://localhost:8080/api/person/1<person id="1">
<firstName>Sam</firstName>
<lastName>Hill</lastName>
</person>404) is returned.<error>Person with id 3 not found</error>
1 by PUTting a new representation to
http://localhost:8080/api/person/1.PUT /api/person/1 HTTP/1.1
Content-Type: application/xml
Accept: application/xml
Host: localhost:8080
Content-Length: 85<person>
<firstName>Samuel</firstName>
<lastName>Hill</lastName>
</person>HTTP/1.1 200 OK Content-Type: application/xml Transfer-Encoding: chunked Server: Jetty(6.1.14)<?xml version="1.0" encoding="UTF-8"?> <person id="1"> <firstName>Samuel</firstName> <lastName>Hill</lastName> </person>
1.<list>
<person id="1">
<firstName>Samuel</firstName>
<lastName>Hill</lastName>
</person>
<person id="2">
<firstName>Fabien></firstName>
<lastName>Barel</lastName>
</person>
</list>1 by sending a DELETE request to http://localhost:8080/api/person/1.DELETE /api/person/1 HTTP/1.1 Accept: application/xml Host: localhost:8080
<list>
<person id="2">
<firstName>Fabien></firstName>
<lastName>Barel</lastName>
</person>
</list>9.3 Using GORM
This section explains what's happening behind the scenes of the scaffolding example and how GORM inside JAX-RS resource classes.PersonCollectionResource.groovy
Here's the source code forPersonCollectionResource.groovy.
package helloimport static org.grails.jaxrs.response.Responses.*import javax.ws.rs.Consumes import javax.ws.rs.GET import javax.ws.rs.Produces import javax.ws.rs.Path import javax.ws.rs.PathParam import javax.ws.rs.POST import javax.ws.rs.core.Response@Path('/api/person') @Consumes(['application/xml','application/json']) @Produces(['application/xml','application/json']) class PersonCollectionResource { @POST Response create(Person dto) { created dto.save() } @GET Response readAll() { ok Person.findAll() } @Path('/{id}') PersonResource getResource(@PathParam('id') String id) { new PersonResource(id:id) } }
org.grails.jaxrs.response.Responses. This
is a helper class provided by the plugin that implements a very simple DSL consisting of elements created and ok.
Supported content types for requests and responses are application/xml and application/json. This is given by the
class-level Consumes and Produces annotations.The PersonCollectionResource class responds to HTTP operations that are related to person lists. The URL of the person
list is http://localhost:8080/api/person where the /api/person path is defined by the class-level
Path('/api/person') annotation.
- The
createmethod responds to POST requests by storing a newPersonobject in the database using GORM. The XML or JSON request entity is converted by the plugin to aPersondomain object and passed to the method via a dto parameter. The conversion is done by a domain object provider. The persisted domain object is passed to the created method which creates a response from it using the JAX-RS API (see source code for details). The created method constructs a URI for theLocationresponse header from the domain objectid. ThePersonobject is set to the Response entity. - The
readAllmethod responds to GET requests and returns a person list. Again we use GORM to get all person objects from the database and pass that list as argument to theokmethod. This method uses the JAX-RS API to create a response (see link source code for details). - The
getResourcemethod creates another JAX-RS resource whenever a request to the URI templatehttp://localhost:8080/api/person/{id}is made. Theidpath parameter in the template is bound to the id parameter of thegetResourcemethod. The created JAX-RSPersonResourceis then used by the JAX-RS runtime to handle the request to the person with the given id.
PersonResource.groovy
Here's the source code forPersonResource.groovy.package helloimport static org.grails.jaxrs.response.Responses.*import javax.ws.rs.Consumes import javax.ws.rs.DELETE import javax.ws.rs.GET import javax.ws.rs.Produces import javax.ws.rs.PUT import javax.ws.rs.core.Responseimport org.grails.jaxrs.provider.DomainObjectNotFoundException@Consumes(['application/xml','application/json']) @Produces(['application/xml','application/json']) class PersonResource { def id @GET Response read() { def obj = Person.get(id) if (!obj) { throw new DomainObjectNotFoundException(Person.class, id) } ok obj } @PUT Response update(Person dto) { def obj = Person.get(id) if (!obj) { throw new DomainObjectNotFoundException(Person.class, id) } obj.properties = dto.properties ok obj } @DELETE void delete() { def obj = Person.get(id) if (obj) { obj.delete() } } }
id property is set during construction of the resource and is used for database operations. This class implements
the methods read, update and delete to handle GET, PUT and DELETE requests, respectively. It also uses GORM for
database operations and relies on helper methods of org.grails.jaxrs.response.Responses to create responses via the
JAX-RS API.If there's no person with given id in the database, a DomainObjectNotFoundException is thrown. This exception class
generates a custom 404 response using the JAX-RS API (see source code for details).
9.4 Applying Filters
Grails filters can be applied to JAX-RS resources as well. For example, to add a filter for the/api/test/ URL
pattern, create a file TestFilters.groovy under grails-app/conf that looks like:Filters have been deprecated in Grails 3. This documentation will be updated with details about using interceptors.
class TestFilters { def filters = { testUris(uri:'/api/test/**') {
before = {
// do some preprocessing
}
after = {
// do some postprocessing
} }
}
}9.5 Service Injection
Services can be auto-injected into resource and provider objects by name. Assue we have a service class namedTestService.groovy in grails-app/services that looks like:package helloclass TestService { String greet(String name) { 'Hello ' + (name ? name : 'unknown') } }
testService property like in the following resource class.package hello@Path('/api/test') class TestResource { def testService // injected @GET @Produces('text/plain') String getTestRepresentation(@QueryParam('name') String name) { testService.greet(name) } }
9.6 Google App Engine
This section describes how to get the hello world example running on Google App Engine.This section has been untested with Grails 3. If this documentation is out of date, please submit an issue or contribute updated documentation in a pull request.
- Install the Grails app-engine plugin.
- Create a Grails application and JAX-RS resource as described in the hello world example.
- Be sure to use the jaxrs-restlet implementation plugin.
- Add the following entries to the Grails configuration:
// replace <application-name> with the // actual App Engine application name google.appengine.application='<application-name>'
- Open a shell at the root directory of the hello world application.
- Set the application version to 1 with:
grails set-version 1
- Run the application locally with
grails app-engine run. - Enter the URL
http://localhost:8080/test?name=Testerinto your browser and the browser window should display Hello Tester . - Package the plugin with
grails app-engine package. - Deploy the application with the
appcfgcommand-line tool from your App Engine SDK. - On Linux, enter:
$APPENGINE_HOME/bin/appcfg.sh update ./target/war
- On Windows, enter:
%APPENGINE_HOME%binappcfg.cmd update .targetwar
- When prompted enter email and password to authenticate at Google App Engine.
- Once deployment is done go enter the URL
http://<application-name>.appspot.com/test?name=Testerinto your browser and the browser window should display Hello Tester . It may take 10-20 seconds for Google App Engine to initialize the Grails application the first time. Subsequent requests are served much faster.
Using scaffolding together with the gorm-jpa plugin is not supported at the moment. A related feature request has already been added to the issue tracker.