Share
Embed
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
Transcript
51 slides
-
Spring HATEOAS – Hypermedia APIs with Spring Oliver Drotbohm, Greg
Turnquist October 7–10, 2019 Austin Convention Center -
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. -
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? ! -
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? -
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 -
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 -
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) -
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 -
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 -
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! -
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? -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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) { … } } -
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 -
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 -
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 -
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 -
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 + -
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 + -
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 -
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 -
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? -
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 -
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? -
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“. -
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 -
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 -
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 -
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. -
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 -
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 -
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 -
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? -
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 -
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 -
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! -
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"); -
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 -
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 -
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 -
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