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
    Creating RESTful Web Services
    with Grails 3
    Jennifer Strater
    Object Partners, Inc.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. @jennstrater
    #Devoxx #restwithgrails3

    View Slide

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

    View Slide

  9. @jennstrater
    #Devoxx #restwithgrails3

    View Slide

  10. @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

    View Slide

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

    View Slide

  12. @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

    View Slide

  13. @jennstrater
    #Devoxx #restwithgrails3
    package gr8data


    import grails.rest.*


    @Resource(readOnly = false, formats = ["json", "xml"])

    class Country {


    }
    Country.groovy

    View Slide

  14. @jennstrater
    #Devoxx #restwithgrails3
    package gr8data


    import grails.rest.*


    @Resource(+ uri="/countries", readOnly = false, formats = ["json", "xml"])

    class Country {

    + String name

    + String abbreviation

    + String continent

    }

    View Slide

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

    View Slide

  16. @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

    View Slide

  17. @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

    View Slide

  18. @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

    View Slide

  19. @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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. @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

    View Slide

  24. @jennstrater
    #Devoxx #restwithgrails3
    package gr8data


    class Company {


    static constraints = {

    }

    }

    RESTful Controllers

    View Slide

  25. @jennstrater
    #Devoxx #restwithgrails3
    package gr8data


    class Company {
    + String name
    + Country country


    static constraints = {

    }

    }

    RESTful Controllers

    View Slide

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

    View Slide

  27. @jennstrater
    #Devoxx #restwithgrails3
    package gr8data


    import grails.rest.*

    import grails.converters.*


    class CompanyController extends RestfulController {

    static responseFormats = ["json", "xml"]

    CompanyController() {

    super(Company)

    }

    }


    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. @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

    View Slide

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

    View Slide

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

    View Slide

  35. @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

    View Slide

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

    View Slide

  37. @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

    View Slide

  38. @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

    View Slide

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

    View Slide

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

    View Slide

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

    • Content Type

    View Slide

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

    View Slide

  43. @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)"
    }
    }

    View Slide

  44. @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

    View Slide

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

    View Slide

  46. @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

    View Slide