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.
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? !
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)
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
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
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Order cancellation policy Demo
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
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
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
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
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
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
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
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
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
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 +
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 +
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
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?
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
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?
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“.
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
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
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
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.
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
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
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?
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
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
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!
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");
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
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
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Thanks! Oliver Drotbohm ƀ [email protected] / odrotbohm
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Resources Rest beyond the obvious