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

Spring HATEOAS – Hypermedia APIs with Spring

Spring HATEOAS – Hypermedia APIs with Spring

Slides about how to build hypermedia based APIs with Spring HATEOAS. Presented at SpringOne 2019 in Austin, TX.

Oliver Drotbohm

October 09, 2019
Tweet

More Decks by Oliver Drotbohm

Other Decks in Programming

Transcript

  1. Spring HATEOAS – Hypermedia APIs with Spring Oliver Drotbohm, Greg

    Turnquist
 
 October 7–10, 2019 Austin Convention Center
  2. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring HATEOAS helps building
 hypermedia-based, REST web services with Spring MVC.
  3. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring HATEOAS helps building
 hypermedia-based, REST web services with Spring MVC. What does that even mean? !
  4. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ What is Hypermedia?
  5. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ { "createdAt" : "…", "lineItems" : [ … ], "total" : …, … } 5 Application data
  6. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ { "_links" : { "self" : { "href" : "…", }, "customer" : { "href" : "…" }, … }, "createdAt" : "…", "lineItems" : [ … ], "total" : …, … "_embedded" : { "customer" : { … } } } 6 Hypermedia Elements Application data
  7. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Hypermedia Elements Static Related resources Static IANA link relations (self)
  8. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ { "_links" : { "self" : { "href" : "…", }, "customer" : { "href" : "…" }, … }, "createdAt" : "…", "lineItems" : [ … ], "total" : …, … "_embedded" : { "customer" : { … } } } "self" : { "href" : "…", }, "customer" : { "href" : "…" }, 10 Hypermedia Elements Application data IANA link relations Static relationships
  9. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Hypermedia Elements Static Dynamic Related resources Static IANA link relations (self) Links representing the resource's state
  10. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ { "_links" : { "self" : { "href" : "…", }, "payment" : { "href" : "…" }, … }, "createdAt" : "…", "lineItems" : [ … ], "total" : …, … "_embedded" : { "customer" : { … } } } "self" : { "href" : "…", }, "payment" : { "href" : "…" }, 13 Hypermedia As The Engine Of Application State Appears only if the
 order can be paid!
  11. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Why Hypermedia?
  12. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ RESTBUCKS payment expected preparing cancelled ready completed 1 2 3 4 5 6 From: REST In Practice – O'Reilly Sample code @ GitHub: Spring RESTBucks
  13. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ RESTBUCKS payment expected preparing cancelled ready completed 1 2 3 4 5 6 7
  14. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Order cancellation policy Demo
  15. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring HATEOAS
  16. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Representation Models Links & Affordances Media types Client support
  17. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Representation Models General support ResourceSupport Item resource
 e.g. /customer/{id} Resources<T> Resource<T> Collection resource
 e.g. /customers 0.x
  18. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ General support RepresentationModel Item resource
 e.g. /customer/{id} CollectionRepresentationModel<T> EntityRepresentationModel<T> Collection resource
 e.g. /customers Representation Models 1.0
  19. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Links & Affordances var link = new Link("/something");
 
 assertThat(link.getHref()).isEqualTo("/something");
 assertThat(link.getRel()).isEqualTo(IanaLinkRelations.SELF);
 link = new Link("/something", "my-rel");
 
 assertThat(link.getHref()).isEqualTo("/something");
 assertThat(link.getRel()).isEqualTo(LinkRelation.of("my-rel")); Plain link with
 self relation Dedicated
 relation
  20. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Links & Affordances var link = new Link("/{segment}/something{?parameter}");
 
 assertThat(link.isTemplated()).isTrue(); 
 assertThat(link.getVariableNames()).contains("segment", "parameter");
 var values = Map.of("segment", "path",
 "parameter", 42); 
 assertThat(link.expand(values).getHref()) 
 .isEqualTo("/path/something?parameter=42"); Template
 variables Expand the template
 using the given values
  21. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Links & Affordances @Controller class PersonController { @GetMapping("/people") HttpEntity<PersonModel> showAll() { … } @GetMapping("/people/{person}") HttpEntity<PersonModel> show(@PathVariable Long person) { … } }
  22. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Links & Affordances import static org.sfw.hateoas.server.mvc.WebMvcLinkBuilder.*; var link = linkTo(methodOn(PersonController.class).show(2L))
 .withRel("people"); 
 assertThat(link.getRel()).isEqualTo(LinkRelation.of("people")); assertThat(link.getHref()).endsWith("/people/2"); Type-safe reference to the controller method
  23. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Links & Affordances @Value
 class PaymentForm {
 CreditCardNumber number;
 }
 class PaymentController {
 
 @PutMapping("/order/{id}/payment")
 ResponseEntity<?> submitPayment(@PathVariable("id") Order order,
 @RequestBody PaymentForm form) { … }
 } Lombok's @Value to generate constructor Inbound payload is a PaymentForm
  24. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Links & Affordances @Controller class PaymentMetadataController { @GetMapping(path = "/docs/payment", …) ResponseEntity<RepresentationModel<? >> getPaymentMetadata() { var selfLink = ….andAffordance( afford(methodOn(PaymentController.class).submitPayment(…))); return ResponseEntity.ok(new RepresentationModel <>(selfLink)); } } Adds affordance metadata to the link by inspecting the method
  25. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Links & Affordances var methodInvocation = methodOn(EmployeeController.class).all();
 var link = Affordances.of(linkTo(methodInvocation).withSelfRel()) 
 .afford(HttpMethod.POST)
 .withInputAndOutput(EmployeeDTO.class)
 .withName("create")
 .andAfford(HttpMethod.GET) 
 .withOutput(EmployeeDTO.class)
 .addParameters(
 QueryParameter.optional("name"),
 QueryParameter.optional("role"))
 .withName("search").toLink(); POST to the resource to
 create a new Employee Access all Employees
 and potentially filter
  26. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ asdasd Media types Representation model Jackson customizations JSON Output +
  27. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Affordance model factory Jackson customizations asdasd Media types JSON Output +
  28. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HAL-FORMS An example
  29. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Links & Affordances var model = new EntityResourceModel <>(order);
 
 
 
 model.addLink(linkTo(…).withRel("restbucks:payment")); Create representation model Add links pointing to controller methods
  30. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HAL-FORMS { "_links" : { "restbucks:payment" : { … }, } } What are we supposed
 to do with this?
  31. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Links & Affordances public @Bean CurieProvider curieProvider() { return new DefaultCurieProvider("restbucks", UriTemplate.of("/docs/{rel}")); } The link namespace Where to find docs
 describing semantics
  32. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HAL-FORMS { "_links" : { "restbucks:payment" : { … }, "curies" : [{ "name" : "restbucks", "href" : "http: //api.acme.com/docs/{rel}", "templated" : true }] } } "Find metadata about the payment resource at
 http://…/docs/payment!" What are we supposed
 to do with this?
  33. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HAL-FORMS { … "_templates" : { "default" : { "method" : "put", "properties" : [{ "name" : "number" }] } } } Use an HTTP PUT request. Provide a field named „number“.
  34. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HAL-FORMS @Value class PaymentForm { CreditCardNumber number; } class PaymentController { @PutMapping("/order/{id}/payment") ResponseEntity<?> submitPayment(@PathVariable("id") Order order,
 @RequestBody PaymentForm form) { … } } Source for form details PUT derived from this
  35. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HAL-FORMS @Value class PaymentForm { CreditCardNumber number; } We need to expose the pattern
  36. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HAL-FORMS @Value class PaymentForm { CreditCardNumber number; } @Bean HalFormsConfiguration halFormsConfiguration() { HalFormsConfiguration configuration = new HalFormsConfiguration(); return configuration.registerPattern(CreditCardNumber.class, CreditCardNumber.REGEX); } Expose the pattern to the 
 Jackson customizations
  37. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HAL-FORMS { … "_templates" : { "default" : { "method" : "put", "properties" : [{ "name" : "number", "regex" : "[0-9]{16}" }] } } } Use an HTTP PUT request. Provide a field named „number“
 matching the given pattern.
  38. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ HAL-FORMS Demo
  39. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Hypermedia Elements Inline resource Separate resource No additional requests Metadata repeatedly transmitted Cachability Additional requests
  40. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Media types JSON-LD, JSON-API Contributions welcome! HAL
 http://bit.ly/hal-spec2 0.x 1.0 Collection+JSON
 http://bit.ly/collection-json-spec 1.0 ALPS
 http://bit.ly/alps-spec 0.x 1.0 HAL / HAL+FORMS
 http://bit.ly/hal-forms UBER
 http://bit.ly/uber-spec
  41. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ How to create a
 custom media type?
  42. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Custom media types interface HypermediaMappingInformation { List<MediaType> getMediaTypes(); default Module getJacksonModule() { … } default ObjectMapper configureObjectMapper(ObjectMapper mapper) {…} } The media types you
 want to support. A Jackson module to
 customize output OR a custom ObjectMapper
 for advanced needs
  43. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Custom media types public interface AffordanceModelFactory { MediaType getMediaType(); AffordanceModel getAffordanceModel(String name,
 Link link,
 HttpMethod httpMethod,
 InputPayloadMetadata inputType, List<QueryParameter> queryMethodParameters,
 PayloadMetadata outputType); } Build a media-type
 specific model
  44. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Custom media types META-INF/spring.factories org.sfw.hateoas.mediatype.AffordanceModelFactory=\ com.acme.MyCustomAffordanceModelFactory Look at the existing
 ones for inspiration!
  45. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Client support var uri = URI.create("http: //localhost:8080/api/");
 var traverson = new Traverson(uri, MediaTypes.HAL_JSON);
 var name = traverson.follow("movies", "movie", "actor")
 .withTemplateParameters(Map.of("user", 27))
 .toObject("$.name");
  46. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Client support var content = 
 "{'_links' : {'payment' : {'href' : '/orders/1/payment'}}}";
 var discoverer = new HalLinkDiscoverer();
 var link = discoverer.findLinkWithRel("payment", content);
 assertThat(link.getRel(), is("payment"));
 assertThat(link.getHref(), is("/orders/1/payment"));
 var discoverers = context.getBean(LinkDiscoverers.class);
 var discoverer = 
 discoverers.getLinkDiscovererFor(MediaTypes.HAL_JSON); Media-type specific
 discoverer All available ones
 in the context
  47. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Miscellaneous RepresentationModelAssembler
 I18N support in media types
 LinkRelationProvider API
 Migration script 0.x
 Kotlin extensions
  48. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Thanks! Oliver Drotbohm ƀ [email protected] / odrotbohm
  49. Unless otherwise indicated, these slides are © 2013-2019 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Resources Rest beyond the obvious