Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Creating RESTful Web Services with Grails 3

jlstrater
November 09, 2015

Creating RESTful Web Services with Grails 3

Integration is a key piece of modern software development. Whether integrating with external vendors or providing APIs for front-end technologies like AngularJS or ReactJS, REST is a popular choice. This session will walk through how and why using the Grails framework to create a RESTful API is easy and efficient. Attendees should have a basic understanding of REST and experience with a web application framework like Grails.

jlstrater

November 09, 2015
Tweet

More Decks by jlstrater

Other Decks in Technology

Transcript

  1. @jennstrater #Devoxx #restwithgrails3 Agenda • Getting Started • Resource Annotation

    • RESTful Controllers • Key Features • Supporting Multiple Media Types • Custom Response Formats • Versioning
  2. @jennstrater #Devoxx #restwithgrails3 New in Grails 3 • Based on

    Spring Boot • Switched to Gradle for build system • Major structural changes • configuration • tests • scripts directory
  3. @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+)
  4. @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
  5. @jennstrater #Devoxx #restwithgrails3 Data Model • Company • name •

    Country • name • abbreviation • continent • various stats
  6. @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
  7. @jennstrater #Devoxx #restwithgrails3 package gr8data
 
 import grails.rest.*
 
 @Resource(readOnly

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

    uri="/countries", readOnly = false, formats = ["json", "xml"])
 class Country {
 + String name
 + String abbreviation
 + String continent
 }
  9. @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
  10. @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
  11. @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
  12. @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
  13. @jennstrater #Devoxx #restwithgrails3 PATCH curl -H "Content-Type: application/json" -X PATCH

    -d "{"abbreviation": "US"}" http://localhost:8080/countries/2 status: 200 OK
  14. @jennstrater #Devoxx #restwithgrails3 Resource Annotation Recap • Automatic Mappings •

    No controllers created • URLMappings.groovy not updated • HTTP Status Codes • HTTP Methods
  15. @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
  16. @jennstrater #Devoxx #restwithgrails3 package gr8data
 
 class Company {
 


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

    String name + Country country
 
 static constraints = {
 }
 }
 RESTful Controllers
  18. @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") …
  19. @jennstrater #Devoxx #restwithgrails3 package gr8data
 
 import grails.rest.*
 import grails.converters.*


    
 class CompanyController extends RestfulController {
 static responseFormats = ["json", "xml"]
 CompanyController() {
 super(Company)
 }
 }

  20. @jennstrater #Devoxx #restwithgrails3 URI Customization • Resource Annotation • parameters

    • i.e. @Resouce(uri="/countries", readOnly=true) • UrlMappings.groovy • resources • uri • methods • response formats • etc
  21. @jennstrater #Devoxx #restwithgrails3 HTTP Methods • via mappings • "/custom"(controller="custom",

    action=“index”, method=“GET") • via controllers • static allowedMethods = [save: "POST", update: "PUT"]
  22. @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
  23. @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
  24. @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
  25. @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
  26. @jennstrater #Devoxx #restwithgrails3 JSON view* • JSON views replace GSPs

    in web-api profile • Convention Based • index.gsp -> index.gson *Grails 3.1+
  27. @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")
  28. @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)" } }
  29. @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
  30. @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!
  31. @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