Slide 1

Slide 1 text

Spring HATEOAS – Hypermedia APIs with Spring Oliver Drotbohm, Greg Turnquist
 
 October 7–10, 2019 Austin Convention Center

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

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.

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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?

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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)

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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!

Slide 12

Slide 12 text

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?

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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 Resource Collection resource
 e.g. /customers 0.x

Slide 19

Slide 19 text

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 EntityRepresentationModel Collection resource
 e.g. /customers Representation Models 1.0

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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 showAll() { … } @GetMapping("/people/{person}") HttpEntity show(@PathVariable Long person) { … } }

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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 +

Slide 28

Slide 28 text

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 +

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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?

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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?

Slide 34

Slide 34 text

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“.

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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.

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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?

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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 queryMethodParameters,
 PayloadMetadata outputType); } Build a media-type
 specific model

Slide 45

Slide 45 text

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!

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

Klient https://github.com/odrotbohm/klient

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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 ƀ odrotbohm@pivotal.io / odrotbohm

Slide 51

Slide 51 text

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