Slide 1

Slide 1 text

for HATEOAS Kai Tödter

Slide 2

Slide 2 text

Who am I? ▪ Principal Key Expert at Siemens Smart Infrastructure ▪ Web Technology Fan ▪ Open Source Lover ▪ E-mail: [email protected] ▪ Twitter: twitter.com/kaitoedter 4/29/2022 2 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License.

Slide 3

Slide 3 text

HATEOAS ▪ Is for “Hypermedia As The Engine Of Application State” ▪ Very hard to pronounce ☺ ▪ Key concept of REST ▪ WIKIPEDIA: With HATEOAS, a client interacts with a network application whose application servers provide information dynamically through hypermedia 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 3

Slide 4

Slide 4 text

JSON:API Web site: jsonapi.org “JSON:API is designed to minimize both the number of requests and the amount of data transmitted between clients and servers. This efficiency is achieved without compromising readability, flexibility, or discoverability.” 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 4

Slide 5

Slide 5 text

Minimal JSON:API Example { "data": { "id": "1", "type": "movies", "attributes": { "title": "The Shawshank Redemption", "year": 1994, "rating": 9.3, "rank": 1 } }, "links": { "self": "https://mymovies.com/api/movies/1" } } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 5

Slide 6

Slide 6 text

Spring HATEOAS ▪ Spring basic framework for REST with Hypermedia support ▪ Supports generic Hypermedia API ▪ Build-in Support for Representations like HAL, HAL-FORMS, UBER, Collection+JSON, … ▪ Community-based media types: JSON:API, Siren ▪ https://docs.spring.io/spring-hateoas/docs/current- SNAPSHOT/reference/html/ 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 6

Slide 7

Slide 7 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 7 Links

Slide 8

Slide 8 text

Links ▪ Essential for hypermedia ▪ In REST: How to navigate to a REST resource ▪ Link semantic/name is called link relation ▪ The relation between a REST resource and the target REST resource ▪ Links are well known from HTML, like link text 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 8

Slide 9

Slide 9 text

Links in Spring HATEOAS Link link = new Link("/my-url"); ▪ A link automatically has a self relation Link link = new Link("/my-url", "my-rel"); ▪ A link with my-rel relation 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 9

Slide 10

Slide 10 text

Link Relations ▪ Many Link relations are standardized by IANA ▪ IANA = Internet Assigned Numbers Authority ▪ https://www.iana.org/assignments/link- relations/link-relations.xhtml ▪ Examples: self, item, next, last, … ▪ Recommendation: Before creating a custom name for a link relation, look up the IANA list! 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 10

Slide 11

Slide 11 text

Links are great! ▪ For providing navigation to useful other REST resources ▪ For providing domain knowledge to the REST clients, so that they don’t have to compute domain state on there own 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 11

Slide 12

Slide 12 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 12 Representation Models

Slide 13

Slide 13 text

Representation Models ▪ REST => Representational State Transfer ▪ Manipulation of resources through their representations ▪ Domain Model != Representation Model ▪ Spring HATEOAS provides RepresentationModel abstraction 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 13

Slide 14

Slide 14 text

Spring HATEOAS RepresentationModel ▪ RepresentationModel ▪ Root class, for REST item resources ▪ CollectionModel ▪ For REST collection resources ▪ EntityModel ▪ Convenient wrapper for converting a domain model into a representation model ▪ PagedModel ▪ Addition to CollectionModel for paged collections 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 14

Slide 15

Slide 15 text

Domain Model Example public class Director { private Long id; private String name; public Director(String name) { this.name = name; } … } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 15

Slide 16

Slide 16 text

Controller Example @GetMapping("/directors/{id}") public ResponseEntity> findOne(@PathVariable Long id) { return repository.findById(id) .map(director -> EntityModel.of(director) .add(linkTo(methodOn(DirectorController.class) .findOne(director.getId())).withSelfRel())) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 16

Slide 17

Slide 17 text

Response in HAL Media Type { "id": 2, "name": "Frank Darabont", "_links": { "self": { "href": "http://localhost:8080/api/directors/2" } } } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 17

Slide 18

Slide 18 text

{json:api} for Spring HATEOAS

Slide 19

Slide 19 text

JSON:API for Spring HATEOAS ▪ Open Source Project ▪ Apache 2 License ▪ https://github.com/toedter/spring-hateoas- jsonapi ▪ Reference Documentation ▪ API Documentation 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 19

Slide 20

Slide 20 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 20

Slide 21

Slide 21 text

Project Dependencies Maven: com.toedter spring-hateoas-jsonapi 1.3.0 Gradle: implementation 'com.toedter:spring-hateoas-jsonapi:1.3.0' 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 21

Slide 22

Slide 22 text

Domain Model Example public class Director { private Long id; private String name; public Director(String name) { this.name = name; } … } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 22

Slide 23

Slide 23 text

Response in JSON:API media type { "data": { "id": "2", "type": "directors", "attributes": { "name": "Frank Darabont" } }, "links": { "self": "http://localhost:8080/api/directors/2" } } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 23

Slide 24

Slide 24 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 24 Annotations

Slide 25

Slide 25 text

Annotations ▪ @JsonApiId to mark a JSON:API id ▪ @JsonApiType to mark a JSON:API type ▪ @JsonApiTypeForClass to mark a class to provide a JSON:API type ▪ @JsonApiRelationships to mark a JSON:API relationship, only used for deserialization ▪ @JsonApiMeta to serialize/deserialize properties to JSON:API meta 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 25

Slide 26

Slide 26 text

Example with Annotations public class Movie { @Id private String myId; @JsonApiType private String myType; @JsonApiMeta private String myMeta; private String title; } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 26

Slide 27

Slide 27 text

Annotations Example (2) EntityModel.of( new Movie("1", "MOVIE", "metaValue", "Star Wars")); will be rendered as { "data": { "id": "1", "type": "MOVIE", "attributes": { "title": "Star Wars" }, "meta": { "myMeta": "metaValue" } } } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 27

Slide 28

Slide 28 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 28 Builder

Slide 29

Slide 29 text

JsonApiBuilder Movie movie = new Movie("1", "Star Wars"); final RepresentationModel> jsonApiModel = jsonApiModel() .model(movie) .build(); 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 29

Slide 30

Slide 30 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 30 Relationships

Slide 31

Slide 31 text

Relationships ▪ In JSON:API, relationships between REST resources are made explicit, using the relationship object ▪ Relationships can be to-one or to-many ▪ Relationships must contain at least one of: ▪ links: a links object containing at least one of the following: ▪ self: a link for the relationship itself ▪ related: a related resource link ▪ data: resource linkage with id and type ▪ meta: meta object that contains non-standard meta- information about the relationship 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 31

Slide 32

Slide 32 text

Build Relationships Movie movie = new Movie("1", "Star Wars"); Director director = new Director("1", "George Lucas"); final RepresentationModel> jsonApiModel = jsonApiModel() .model(movie) .relationship("directors", director) .build(); 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 32

Slide 33

Slide 33 text

Relationsship Example { "data": { "id": "1", "type": "movies", "attributes": { "title": "Star Wars" }, "relationships": { "directors": { "data": { "id": "1", "type": "directors" } } } } } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 33

Slide 34

Slide 34 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 34 Inclusion

Slide 35

Slide 35 text

Inclusion of Related Resources ▪ With included, you can include the content of related recourses in the compound document ▪ The JsonApiBuilder supports adding ▪ A single included resource ▪ A collection of included resources ▪ The builder assures that included resources with same id and type appear only ONCE 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 35

Slide 36

Slide 36 text

Inclusion Example for (Movie movie : pagedResult.getContent()) { jsonApiModelBuilder.included(movie.getDirectors()); } "included": [ { "id": "1", "type": "directors", "attributes": { "name": "Lana Wachowski" } }, … 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 36

Slide 37

Slide 37 text

Sparse Fieldsets Convenient way to specify which ▪ Attributes of Resources ▪ Relationships (by name) ▪ Attributes of included Relationships will be included in the JSON response 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 37

Slide 38

Slide 38 text

Controller for Sparse Fieldset In a REST controller, a method with HTTP- mapping could provide an optional request attribute for each sparse fieldset @GetMapping("/movies") public ResponseEntity> findAll( @RequestParam(value = "included", required = false) String[] included, @RequestParam(value = "fields[movies]", required = false) String[] fieldsMovies) { 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 38

Slide 39

Slide 39 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 39 Sparse Fieldsets Demo

Slide 40

Slide 40 text

Meta ▪ JSON:API Meta can be added using the builder or by using the @JsonApiMeta annotation ▪ Paging information Meta can be added automatically => Use PagedModel 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 40

Slide 41

Slide 41 text

Pagination Example … "links": { "self": "http://localhost/movies", "first": "http://localhost/movies?page[number]=0&page[size]=2", "prev": "http://localhost/movies?page[number]=0&page[size]=2", "next": "http://localhost/movies?page[number]=2&page[size]=2", "last": "http://localhost/movies?page[number]=49&page[size]=2" }, "meta": { "page": { "number": 1, "size": 2, "totalPages": 50, "totalElements": 100 } } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 41

Slide 42

Slide 42 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 42 Configuration

Slide 43

Slide 43 text

Configuration (1) You can configure ▪ If the JSON:API version should be rendered automatically, the default is false. ▪ If JSON:API types should be rendered as pluralized or non pluralized class names. ▪ The default is pluralized ▪ If JSON:API types should be rendered as lower cased or original class names. ▪ The default is lower cased ▪ If page information of a PagedModel should be rendered automatically as JSON:API meta object. ▪ The default is true 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 43

Slide 44

Slide 44 text

Configuration (2) You can configure ▪ If a specific Java class should be rendered with a specific JSON:API type. ▪ A lambda expression to add additional configuration to the Jackson ObjectMapper used for serialization. ▪ Experimental: Render Spring HATEOAS affordances as JSON:API link meta. 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 44

Slide 45

Slide 45 text

Configuration Example @Bean JsonApiConfiguration jsonApiConfiguration() { return new JsonApiConfiguration() .withJsonApiVersionRendered(true) .withPluralizedTypeRendered(false) .withLowerCasedTypeRendered(false) .withTypeForClass(MyMovie.class, "my-movies") .withObjectMapperCustomizer( objectMapper -> objectMapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true)); } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 45

Slide 46

Slide 46 text

Error Handling To create JSON:API compliant error messages, you can use JsonApiErrors and JsonApiError return ResponseEntity.badRequest().body( JsonApiErrors.create().withError( JsonApiError.create() .withAboutLink("http://movie-db.com/problem") .withTitle("Movie-based problem") .withStatus(HttpStatus.BAD_REQUEST.toString()) .withDetail("This is a test case"))); 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 46

Slide 47

Slide 47 text

Error Example { "errors": [ { "links": { "about": "http://movie-db.com/problem" }, "status": "400 BAD_REQUEST", "title": "Movie-based problem", "detail": "This is a test case" } ] } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 47

Slide 48

Slide 48 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 48 Deserialization

Slide 49

Slide 49 text

Deserialization ▪ We want to deserialize JSON input directly to Spring HATEOAS representation models, e.g. for ▪ HTTP POST ▪ HTTP PATCH 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 49

Slide 50

Slide 50 text

Deserialization Example (1) { "data": { "type": "movies", "attributes": { "title": "New Movie" }, "relationships": { "directors": { "data": [ { "id": "1", "type": "directors" }, { "id": "2", "type": "directors" } ] } } } } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 50

Slide 51

Slide 51 text

Deserialization Example (2) public class MovieWithDirectors extends Movie { @JsonApiType String myType = "movies"; @JsonIgnore @JsonApiRelationships("directors") List directors; <= ONLY Director id will be set! } 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 51

Slide 52

Slide 52 text

Conclusion With JSON:API for Spring HATEOAS, it is very easy to support JSON:API (serialization + deserialization) out of the box. With the builder, special JSON:API features like relationships and sparse fieldsets are supported as well. 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 52

Slide 53

Slide 53 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 53 Affordances

Slide 54

Slide 54 text

“ The affordances of the environment are what it offers …​ what it provides or furnishes, either for good or ill. The verb 'to afford' is found in the dictionary, but the noun 'affordance' is not. I have made it up.” James J. Gibson The Ecological Approach to Visual Perception (1986) 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 54

Slide 55

Slide 55 text

Affordances ▪ With Affordances, possible actions on a REST resource (or Link relation) can be described in more detail, e.g. ▪ What can you do with a REST resource? ▪ Create, Update, Delete, … ▪ Which parameters are mandatory or optional ▪ How would (internationalized) labels be displayed in a user interface ▪ … 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 55

Slide 56

Slide 56 text

Spring HATEOAS Affordances Example © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 56 final Affordance newMovieAffordance = afford(methodOn(MovieController.class).newMovie(null)); Link selfLink = linkTo(MovieController.class).slash("movies" + uriParams + "page[number]=" + pagedResult.getNumber() + "&page[size]=" + pagedResult.getSize()) .withSelfRel() .andAffordance(newMovieAffordance);

Slide 57

Slide 57 text

Rendered Affordance (1) 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 57 "links": { "self": { "href": "http://localhost:8080/api/movies?page[number]=0&page[size]=10", "meta": { "affordances": [ { "name": "newMovie", "link": { "rel": "newMovie", "href": "http://localhost:8080/api/movies" }, "httpMethod": "POST", ...

Slide 58

Slide 58 text

Rendered Affordance (2) 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 58 "inputProperties": [ { "name": "imdbId", "type": "text" }, … { "name": "title", "type": "text", "required": true }, … ]

Slide 59

Slide 59 text

4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 59 Discussion

Slide 60

Slide 60 text

Links ▪ Spring HATEOAS: https://github.com/spring-projects/spring-hateoas ▪ JSON:API for Spring HATEOAS: https://github.com/toedter/spring-hateoas-jsonapi 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 60

Slide 61

Slide 61 text

License ▪ This work is licensed under a Creative Commons Attribution 4.0 International License. ▪ See http://creativecommons.org/licenses/by/4.0/ 4/29/2022 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 61