Create RESTful Service with Grails 2.4.x
With Grails 2.3 (I assume 2.4 the same) it is really easy to create restful service. This tutorial is pretty simple, I just want to access localhost/StateService/state and get a response in json format:
[{
'id': 1,
'name': 'OH'
…
}]
Create Grails Project
grails create–app StateService
Create Domain Class
create–domain–class edu.osumc.bmi.ird.state.service.State
Edit State.groovy with the attributes
package edu.osumc.bmi.ird.state.service
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
@ToString(includeNames = true, includeFields = true,
excludes = 'dateCreated, lastUpdated, metaClass')
@EqualsAndHashCode
class State {
// gorm default attributes
Long id
Long version
// gorm timestamp
Date dateCreated
Date lastUpdated
String name
String fullName
static constraints = {
name blank: false, nullable: false, unique: true, minSize: 2, maxSize: 2
fullName blank: false, nullable: false, minSize: 3, maxSize: 32
}
}
Bootstrap Some Sample Data
import edu.osumc.bmi.ird.state.service.State
import grails.util.Environment
class BootStrap {
def init = { servletContext ->
switch (Environment.current) {
case Environment.DEVELOPMENT:
seedDevData()
break;
}
}
def destroy = {
}
private void seedDevData() {
State.findByName("OH") ?: new State(name: "OH", fullName: "Ohio")
.save(flush: true, failOnError: true)
State.findByName("MI") ?: new State(name: "MI", fullName: "Michigan")
.save(flush: true, failOnError: true)
State.findByName("CA") ?: new State(name: "CA", fullName: "California")
.save(flush: true, failOnError: true)
assert State.count == 2
}
}
Create a Controller
create–controller edu.osumc.bmi.ird.state.service.State
Make Created Controller RESTful
From Grails document if we want to make a controller be a restful controller, just extending grails.rest.RestfulController.
package edu.osumc.bmi.ird.state.service
import grails.rest.RestfulController
class StateController extends RestfulController<State> {
static responseFormats = ['json', 'xml']
StateController() {
super(State)
}
}
Test the Service
Run the service: run–app in grails interactive mode
Access http://localhost:8080/StateService/state and get the result in json array:
[{"class":"edu.osumc.bmi.ird.state.service.State","id":1,"dateCreated":"2015-04-07T19:21:06Z","fullName":"Ohio","lastUpdated":"2015-04-07T19:21:06Z","name":"OH"},{"class":"edu.osumc.bmi.ird.state.service.State","id":2,"dateCreated":"2015-04-07T19:21:06Z","fullName":"Michigan","lastUpdated":"2015-04-07T19:21:06Z","name":"MI"},{"class":"edu.osumc.bmi.ird.state.service.State","id":3,"dateCreated":"2015-04-07T19:21:06Z","fullName":"California","lastUpdated":"2015-04-07T19:21:06Z","name":"CA"}]
- curl with json response
$>>> curl –X GET –H "Accept:application/json" http://localhost:8080/StateService/state
[{"class":"edu.osumc.bmi.ird.state.service.State","id":1,"dateCreated":"2015-04-07T19:21:06Z","fullName":"Ohio","lastUpdated":"2015-04-07T19:21:06Z","name":"OH"},{"class":"edu.osumc.bmi.ird.state.service.State","id":2,"dateCreated":"2015-04-07T19:21:06Z","fullName":"Michigan","lastUpdated":"2015-04-07T19:21:06Z","name":"MI"},{"class":"edu.osumc.bmi.ird.state.service.State","id":3,"dateCreated":"2015-04-07T19:21:06Z","fullName":"California","lastUpdated":"2015-04-07T19:21:06Z","name":"CA"}]
- curl with xml response
curl –X GET –H "Accept:application/xml" http://localhost:8080/StateService/state
<?xml version="1.0" encoding="UTF-8"?><list><state id="1"><dateCreated>2015-04-07 15:21:06.235 EDT</dateCreated><fullName>Ohio</fullName><lastUpdated>2015-04-07 15:21:06.235 EDT</lastUpdated><name>OH</name></state><state id="2"><dateCreated>2015-04-07 15:21:06.260 EDT</dateCreated><fullName>Michigan</fullName><lastUpdated>2015-04-07 15:21:06.260 EDT</lastUpdated><name>MI</name></state><state id="3"><dateCreated>2015-04-07 15:21:06.267 EDT</dateCreated><fullName>California</fullName><lastUpdated>2015-04-07 15:21:06.267 EDT</lastUpdated><name>CA</name></state></list>
- Testing with Grails Spock
- Create Testing Spec
package edu.osumc.bmi.ird.state.service
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Specification
@TestFor(StateController)
@Mock(State)
class StateControllerSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test index returns all states in json format"() {
given:
new State(name: "OH", fullName: "Ohio")
.save(flush: true, failOnError: true)
new State(name: "MI", fullName: "Michigan")
.save(flush: true, failOnError: true)
new State(name: "CA", fullName: "California")
.save(flush: true, failOnError: true)
when:
request.method = 'GET'
response.format = 'json'
controller.index()
then:
response.status == 200
}
}
Run testing in grails interactive mode: test-app
Add URL Mapping
Now the service is responding to this url: localhost:8080/StateService/state, we want to add a url mapping to make it localhost:8080/StateService/service/state. Edit conf/UrlMappings.groovy :
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
// restful state service
"/service/state"(resources: 'state')
}
}
Now the service can be accessed with both urls.
Exclude Properties from Response
Until now the response contains all fields of the domains which is not the expectation we want. In real situation we’d like to hide some infos like lastUpdated and meta data of the class. To achieve this we need to edit conf/spring/resources.groovy file:
import edu.osumc.bmi.ird.state.service.State
import grails.rest.render.json.JsonRenderer
// Place your Spring DSL code here
beans = {
stateJsonRenderer(JsonRenderer, State) {
excludes = ['class', 'dateCreated']
}
stateXmlRenderer(JsonRenderer, State) {
excludes = ['class', 'dateCreated']
}
}
The bean’s name doesn’t matter, spring just scan the environment and put the beans in the spring context. Now when we access the url we got this:
http://localhost:8080/StateService/service/state
[{"id":1,"fullName":"Ohio","lastUpdated":"2015-04-07T20:01:06Z","name":"OH"},{"id":2,"fullName":"Michigan","lastUpdated":"2015-04-07T20:01:06Z","name":"MI"},{"id":3,"fullName":"California","lastUpdated":"2015-04-07T20:01:06Z","name":"CA"}]
If we only want to retrieve state with id 1:
http://localhost:8080/StateService/service/state/1
{"id":1,"fullName":"Ohio","lastUpdated":"2015-04-07T20:01:06Z","name":"OH"}
That’s it, this is all we need to do to create a restful service with grails 2.4.x. Source code is available at my github: RESTful StateService