Slide 1

Slide 1 text

@jennstrater #Devoxx #restwithgrails3 Creating RESTful Web Services with Grails 3 Jennifer Strater Object Partners, Inc.

Slide 2

Slide 2 text

@jennstrater #Devoxx #restwithgrails3 About Me • Senior Consultant at Object Partners, Inc. • Co-Founder of Gr8Ladies

Slide 3

Slide 3 text

@jennstrater #Devoxx #restwithgrails3 Agenda • Getting Started • Resource Annotation • RESTful Controllers • Key Features • Supporting Multiple Media Types • Custom Response Formats • Versioning

Slide 4

Slide 4 text

@jennstrater #Devoxx #restwithgrails3 New in Grails 3 • Based on Spring Boot • Switched to Gradle for build system • Major structural changes • configuration • tests • scripts directory

Slide 5

Slide 5 text

@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+)

Slide 6

Slide 6 text

@jennstrater #Devoxx #restwithgrails3 Getting Started • gvm use grails 3.0.9 • grails create-app gr8data --profile=web-api

Slide 7

Slide 7 text

@jennstrater #Devoxx #restwithgrails3

Slide 8

Slide 8 text

@jennstrater #Devoxx #restwithgrails3 • grails>run-app

Slide 9

Slide 9 text

@jennstrater #Devoxx #restwithgrails3

Slide 10

Slide 10 text

@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

Slide 11

Slide 11 text

@jennstrater #Devoxx #restwithgrails3 Data Model • Company • name • Country • name • abbreviation • continent • various stats

Slide 12

Slide 12 text

@jennstrater #Devoxx #restwithgrails3 First Endpoint grails> create-domain-resource gr8data.Country | Created grails-app/domain/gr8data/Country.groovy | Created src/test/groovy/gr8data/CountrySpec.groovy

Slide 13

Slide 13 text

@jennstrater #Devoxx #restwithgrails3 package gr8data
 
 import grails.rest.*
 
 @Resource(readOnly = false, formats = ["json", "xml"])
 class Country {
 
 } Country.groovy

Slide 14

Slide 14 text

@jennstrater #Devoxx #restwithgrails3 package gr8data
 
 import grails.rest.*
 
 @Resource(+ uri="/countries", readOnly = false, formats = ["json", "xml"])
 class Country {
 + String name
 + String abbreviation
 + String continent
 }

Slide 15

Slide 15 text

@jennstrater #Devoxx #restwithgrails3 GET • curl http://localhost:8080/countries • []

Slide 16

Slide 16 text

@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

Slide 17

Slide 17 text

@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

Slide 18

Slide 18 text

@jennstrater #Devoxx #restwithgrails3 PUT curl -H "Content-Type: application/json" -X PUT -d "{"name": "United States of America", "abbreviation": "USA", "continent": "North America"}" http://localhost:8080/countries

Slide 19

Slide 19 text

@jennstrater #Devoxx #restwithgrails3 PUT curl -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

Slide 20

Slide 20 text

@jennstrater #Devoxx #restwithgrails3 PATCH curl -H "Content-Type: application/json" -X PATCH -d "{"abbreviation": "US"}" http://localhost:8080/countries/2 status: 200 OK

Slide 21

Slide 21 text

@jennstrater #Devoxx #restwithgrails3 GET curl http://localhost:8080/countries

Slide 22

Slide 22 text

@jennstrater #Devoxx #restwithgrails3 Resource Annotation Recap • Automatic Mappings • No controllers created • URLMappings.groovy not updated • HTTP Status Codes • HTTP Methods

Slide 23

Slide 23 text

@jennstrater #Devoxx #restwithgrails3 RESTful Controllers New Domain Class grails> create-domain-class gr8data.Company | Created grails-app/domain/gr8data/Company.groovy | Created src/test/groovy/gr8data/CompanySpec.groovy

Slide 24

Slide 24 text

@jennstrater #Devoxx #restwithgrails3 package gr8data
 
 class Company {
 
 static constraints = {
 }
 }
 RESTful Controllers

Slide 25

Slide 25 text

@jennstrater #Devoxx #restwithgrails3 package gr8data
 
 class Company { + String name + Country country
 
 static constraints = {
 }
 }
 RESTful Controllers

Slide 26

Slide 26 text

@jennstrater #Devoxx #restwithgrails3 RESTful Controllers grails> 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") …

Slide 27

Slide 27 text

@jennstrater #Devoxx #restwithgrails3 package gr8data
 
 import grails.rest.*
 import grails.converters.*
 
 class CompanyController extends RestfulController {
 static responseFormats = ["json", "xml"]
 CompanyController() {
 super(Company)
 }
 }


Slide 28

Slide 28 text

@jennstrater #Devoxx #restwithgrails3 URI Customization • Resource Annotation • parameters • i.e. @Resouce(uri="/countries", readOnly=true) • UrlMappings.groovy • resources • uri • methods • response formats • etc

Slide 29

Slide 29 text

@jennstrater #Devoxx #restwithgrails3 "/$controller/$action?/$id?(.$format)?"{ constraints { // apply constraints here } } Default Mapping

Slide 30

Slide 30 text

@jennstrater #Devoxx #restwithgrails3 URL Mappings Report grails>url-mappings-report

Slide 31

Slide 31 text

@jennstrater #Devoxx #restwithgrails3 HTTP Methods • via mappings • "/custom"(controller="custom", action=“index”, method=“GET") • via controllers • static allowedMethods = [save: "POST", update: "PUT"]

Slide 32

Slide 32 text

@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

Slide 33

Slide 33 text

@jennstrater #Devoxx #restwithgrails3 Custom Media Types • Add to Mime Types

Slide 34

Slide 34 text

@jennstrater #Devoxx #restwithgrails3 Custom Response Formats • Marshallers/Renderers • registration • bootstrap.groovy • beans • JSON views*

Slide 35

Slide 35 text

@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

Slide 36

Slide 36 text

@jennstrater #Devoxx #restwithgrails3 import grails.converters.JSON import gr8data.CompanyJsonRenderer beans = { companyJsonRenderer CompanyJsonRenderer } conf/spring/resources.groovy

Slide 37

Slide 37 text

@jennstrater #Devoxx #restwithgrails3 class CompanyJsonRenderer extends AbstractRenderer {
 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

Slide 38

Slide 38 text

@jennstrater #Devoxx #restwithgrails3 class CompanyCollectionJsonRenderer implements ContainerRenderer {
 Class getTargetType() { List }
 
 Class getComponentType() { Company }
 
 MimeType[] getMimeTypes() { [MimeType.JSON] as MimeType[] }
 
 void render(List companies, RenderContext context) {
 … }
 } Json Collection Renderer

Slide 39

Slide 39 text

@jennstrater #Devoxx #restwithgrails3 JSON view* • JSON views replace GSPs in web-api profile • Convention Based • index.gsp -> index.gson *Grails 3.1+

Slide 40

Slide 40 text

@jennstrater #Devoxx #restwithgrails3 json { message "Not Found" error 404 } JSON views*

Slide 41

Slide 41 text

@jennstrater #Devoxx #restwithgrails3 Versioning • URI • http://api.example.com/v1/resource/{id} • http://example.com/api/v1/resource/{id} • Custom Header • • Content Type

Slide 42

Slide 42 text

@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")

Slide 43

Slide 43 text

@jennstrater #Devoxx #restwithgrails3 Version By Namespace package 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)" } }

Slide 44

Slide 44 text

@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

Slide 45

Slide 45 text

@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!

Slide 46

Slide 46 text

@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