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
create
method responds to POST requests by storing a newPerson
object in the database using GORM. The XML or JSON request entity is converted by the plugin to aPerson
domain 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 theLocation
response header from the domain objectid
. ThePerson
object is set to the Response entity. - The
readAll
method 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 theok
method. This method uses the JAX-RS API to create a response (see link source code for details). - The
getResource
method creates another JAX-RS resource whenever a request to the URI templatehttp://localhost:8080/api/person/{id}
is made. Theid
path parameter in the template is bound to the id parameter of thegetResource
method. The created JAX-RSPersonResource
is 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=Tester
into your browser and the browser window should display Hello Tester . - Package the plugin with
grails app-engine package
. - Deploy the application with the
appcfg
command-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=Tester
into 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.