Rest with grails 3
-
Upload
jenn-strater -
Category
Technology
-
view
2.046 -
download
0
Transcript of Rest with grails 3
@jennstrater#Devoxx #restwithgrails3
Creating RESTful Web Services with Grails 3
Jennifer Strater Object Partners, Inc.
@jennstrater#Devoxx #restwithgrails3
About Me
• Senior Consultant at Object Partners, Inc.
• Co-Founder of Gr8Ladies
@jennstrater#Devoxx #restwithgrails3
Agenda
• Getting Started
• Resource Annotation
• RESTful Controllers
• Key Features
• Supporting Multiple Media Types
• Custom Response Formats
• Versioning
@jennstrater#Devoxx #restwithgrails3
New in Grails 3
• Based on Spring Boot
• Switched to Gradle for build system
• Major structural changes
• configuration
• tests
• scripts directory
@jennstrater#Devoxx #restwithgrails3
Web API profile
• Introduced in Grails 3.0.5
• Removes Unnecessary Features
• no GSPs
• New Features
• Grails Command line support
• domain classes with resource annotation
• RESTful controllers
• json/gson views* (Grails 3.1+)
@jennstrater#Devoxx #restwithgrails3
Getting Started
• gvm use grails 3.0.9
• grails create-app gr8data --profile=web-api
@jennstrater#Devoxx #restwithgrails3
The Task - Gr8Data
• Capture and display gender ratios at companies using Groovy and related technology around the world
• Currently available at: http://jlstrater.github.io/gr8ladies-d3/
• Data stored in a json file with contributions via github pull requests
• Demo for this talk: http://github.com/jlstrater/gr8data
@jennstrater#Devoxx #restwithgrails3
Data Model
• Company
• name
• Country
• name
• abbreviation
• continent
• various stats
@jennstrater#Devoxx #restwithgrails3
First Endpointgrails> create-domain-resource gr8data.Country | Created grails-app/domain/gr8data/Country.groovy | Created src/test/groovy/gr8data/CountrySpec.groovy
@jennstrater#Devoxx #restwithgrails3
package gr8data import grails.rest.* @Resource(readOnly = false, formats = ["json", "xml"])class Country {}
Country.groovy
@jennstrater#Devoxx #restwithgrails3
package gr8data import grails.rest.* @Resource(+ uri="/countries", readOnly = false, formats = ["json", "xml"])class Country {+ String name + String abbreviation + String continent}
@jennstrater#Devoxx #restwithgrails3
POST (with errors)curl -H "Content-Type: application/json" -X POST \—data "{"name": "Belgium"}" http://localhost:8080/countries
Status: 422 Unprocessable Entity
@jennstrater#Devoxx #restwithgrails3
POST (successful)curl -H "Content-Type: application/json" -X POST -d "{"name": "Belgium", "abbreviation": "BE", "continent": "Europe"}" http://localhost:8080/countries
Status: 201 Created
@jennstrater#Devoxx #restwithgrails3
PUTcurl -H "Content-Type: application/json" -X PUT -d "{"name": "United States of America", "abbreviation": "USA", "continent": "North America"}"
http://localhost:8080/countries
@jennstrater#Devoxx #restwithgrails3
PUTcurl -H "Content-Type: application/json" -X PUT -d "{"name": "United States of America", "abbreviation": "USA", "continent": "North America"}"
http://localhost:8080/countries/2
status: 200 OK
@jennstrater#Devoxx #restwithgrails3
PATCHcurl -H "Content-Type: application/json" -X PATCH -d "{"abbreviation": "US"}"
http://localhost:8080/countries/2
status: 200 OK
@jennstrater#Devoxx #restwithgrails3
Resource Annotation Recap
• Automatic Mappings
• No controllers created
• URLMappings.groovy not updated
• HTTP Status Codes
• HTTP Methods
@jennstrater#Devoxx #restwithgrails3
RESTful ControllersNew Domain Class
grails> create-domain-class gr8data.Company | Created grails-app/domain/gr8data/Company.groovy | Created src/test/groovy/gr8data/CompanySpec.groovy
@jennstrater#Devoxx #restwithgrails3
package gr8data class Company { static constraints = { }}
RESTful Controllers
@jennstrater#Devoxx #restwithgrails3
package gr8data class Company {+ String name+ Country country static constraints = { }}
RESTful Controllers
@jennstrater#Devoxx #restwithgrails3
RESTful Controllersgrails> create-restful-controller gr8data.Company | Created grails-app/controllers/gr8data/CompanyController.groovy
*REMEMBER* to update UrlMappings.groovy+ "/companies"(controller: "company", action: "index", method: "GET")+ "/companies"(controller: "company", action: "save", method: "POST")+ "/companies/$id"(controller: "company", action: "update", method: "PUT")+ "/companies/$id"(controller: "company", action: "patch", method: "PATCH")+ "/companies/$id"(controller: "company", action: "delete", method: "DELETE")…
@jennstrater#Devoxx #restwithgrails3
package gr8data import grails.rest.* import grails.converters.* class CompanyController extends RestfulController { static responseFormats = ["json", "xml"] CompanyController() { super(Company) }}
@jennstrater#Devoxx #restwithgrails3
URI Customization
• Resource Annotation
• parameters
• i.e. @Resouce(uri="/countries", readOnly=true)
• UrlMappings.groovy
• resources
• uri
• methods
• response formats
• etc
@jennstrater#Devoxx #restwithgrails3
"/$controller/$action?/$id?(.$format)?"{ constraints {
// apply constraints here }
}
Default Mapping
@jennstrater#Devoxx #restwithgrails3
HTTP Methods
• via mappings
• "/custom"(controller="custom", action=“index”, method=“GET")
• via controllers
• static allowedMethods = [save: "POST", update: "PUT"]
@jennstrater#Devoxx #restwithgrails3
Default Media Types• xml if not specified
• on mappings and controllers
• formats = ["json","xml"]
• extension
• /countries.xml
• /countries.json
• Requesting a format not included returns 406 Not Acceptable
@jennstrater#Devoxx #restwithgrails3
Custom Response Formats
• Marshallers/Renderers
• registration
• bootstrap.groovy
• beans
• JSON views*
@jennstrater#Devoxx #restwithgrails3
import grails.converters.JSON
class Bootstrap { def init = { servletContext -> JSON.registerObjectMarshaller(Country) {
return [id: it.id,name: it.name, abbrev: it.abbreviation,continent: it.continent
] } }}
grails-app/init/Bootstrap.groovy
@jennstrater#Devoxx #restwithgrails3
import grails.converters.JSONimport gr8data.CompanyJsonRenderer
beans = { companyJsonRenderer CompanyJsonRenderer}
conf/spring/resources.groovy
@jennstrater#Devoxx #restwithgrails3
class CompanyJsonRenderer extends AbstractRenderer<Company> { CompanyJsonRenderer() { super(Company, [MimeType.JSON, MimeType.TEXT_JSON] as MimeType[]) } void render(Company company, RenderContext context) { context.contentType = MimeType.JSON.name JsonBuilder builder = new JsonBuilder(id: company.id, name: company.name, country: company.country.name) builder.writeTo(context.writer) }}
Json Renderer
@jennstrater#Devoxx #restwithgrails3
class CompanyCollectionJsonRenderer implements ContainerRenderer<List, Company> { Class<List> getTargetType() { List } Class<Company> getComponentType() { Company } MimeType[] getMimeTypes() { [MimeType.JSON] as MimeType[] } void render(List companies, RenderContext context) { … }}
Json Collection Renderer
@jennstrater#Devoxx #restwithgrails3
JSON view*
• JSON views replace GSPs in web-api profile
• Convention Based
• index.gsp -> index.gson
*Grails 3.1+
@jennstrater#Devoxx #restwithgrails3
Versioning
• URI
• http://api.example.com/v1/resource/{id}
• http://example.com/api/v1/resource/{id}
• Custom Header
•
• Content Type
@jennstrater#Devoxx #restwithgrails3
Versioning in Grails
• URI
• "/hello/v1"(controller="hello", namespace: "v1")
• "/hello/v2"(controller="hello", namespace: "v2")
• Accept Header
• "/hello"(version:"1.0", controller="hello", namespace: "v1")
• "/hello"(version:"2.0",controller="hello", namespace: "v2")
@jennstrater#Devoxx #restwithgrails3
Version By Namespacepackage gr8data.v1
class HelloController { static namespace = "v1"
def index() {render "Hello, World (v1)"
}}
package gr8data.v2
class HelloController { static namespace = "v2"
def index() {render "Hello, World (v2)"
}}
@jennstrater#Devoxx #restwithgrails3
Security
• Spring Security Rest Plugin by Álvaro Sánchez Mariscal
• Token based authentication
• Greach 2014 Video
• Grails 2 only
• Spring Security Plugin for Grails 3
• OAuth for Spring Boot
• Screencast by Bobby Warner of Agile Orbit
@jennstrater#Devoxx #restwithgrails3
Conclusion
• Grails web-api profile streamlines API creation.
• Grails defaults support HTTP status codes, HTTP methods, custom media types, custom response formats, and versioning fairly easily.
• More exciting changes coming to the web-api profile with Grails 3.1!
@jennstrater#Devoxx #restwithgrails3
To Learn More
• Restful Web Services in Grails 3 at Gr8Conf US
• YouTube / Slides
• Restful Grails 3 at SpringOne2GX by Jeff Brown
• YouTube / Slides
• Grails 3.X Update at SpringOne2GX by Graeme Rocher
• YouTube / Slides
• Grails Documentation
• REST