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

JSON:API für Spring HATEOAS

JSON:API für Spring HATEOAS

This slide deck introduces my Open Source Project "JSON:API für Spring HATEOAS", see https://github.com/toedter/spring-hateoas-jsonapi

A0aae1297a0593c1316abdcdb4131e3a?s=128

Kai Toedter

May 06, 2021
Tweet

Transcript

  1. for HATEOAS Kai Tödter

  2. Who am I?  Principal Key Expert at Siemens Smart

    Infrastructure  Web Technology Fan  Open Source Lover  E-mail: kai@toedter.com  Twitter: twitter.com/kaitoedter 5/6/2021 2 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License.
  3. 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 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 3
  4. 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.” 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 4
  5. 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" } } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 5
  6. 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/ 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 6
  7. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 7 Links
  8. 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 <a href="url">link text</a> 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 8
  9. 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 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 9
  10. 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! 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 10
  11. 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 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 11
  12. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 12 Representation Models
  13. Representation Models  REST => Representational State Transfer  Manipulation

    of resources through their representations  Domain Model != Representation Model  Spring HATEOAS provides RepresentationModel abstraction 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 13
  14. 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 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 14
  15. Domain Model Example public class Director { private Long id;

    private String name; public Director(String name) { this.name = name; } … } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 15
  16. Controller Example @GetMapping("/directors/{id}") public ResponseEntity<EntityModel<Director>> 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()); } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 16
  17. Response in HAL Media Type { "id": 2, "name": "Frank

    Darabont", "_links": { "self": { "href": "http://localhost:8080/api/directors/2" } } } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 17
  18. {json:api} for Spring HATEOAS

  19. JSON:API for Spring HATEOAS  Open Source Project  Apache

    2 License  https://github.com/toedter/spring-hateoas- jsonapi  Reference Documentation  API Documentation 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 19
  20. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 20
  21. Project Dependencies Maven: <dependency> <groupId>com.toedter</groupId> <artifactId>spring-hateoas-jsonapi</artifactId> <version>0.16.1</version> </dependency> Gradle: implementation

    'com.toedter:spring-hateoas-jsonapi:0.16.1' 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 21
  22. Domain Model Example public class Director { private Long id;

    private String name; public Director(String name) { this.name = name; } … } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 22
  23. Response in JSON:API media type { "data": { "id": "2",

    "type": "directors", "attributes": { "name": "Frank Darabont" } }, "links": { "self": "http://localhost:8080/api/directors/2" } } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 23
  24. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 24 Annotations
  25. 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  The JSON:API type is a required value of this annotation  @JsonApiRelationships to mark a JSON:API relationship  This annotation is not used for serialization but for deserialization 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 25
  26. Example with Annotations public class Movie { @JsonApiId private String

    myId; private String title; @JsonApiType private String myType; } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 26
  27. Annotations Example (2) EntityModel.of(new Movie("1", "Star Wars", "MOVIE"))); will be

    rendered as { "data": { "id": "1", "type": "MOVIE", "attributes": { "title": "Star Wars" } } } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 27
  28. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 28 Builder
  29. JsonApiBuilder Movie movie = new Movie("1", "Star Wars"); final RepresentationModel<?>

    jsonApiModel = jsonApiModel() .model(movie) .build(); 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 29
  30. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 30 Relationships
  31. 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 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 31
  32. 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(); 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 32
  33. Relationsship Example { "data": { "id": "1", "type": "movies", "attributes":

    { "title": "Star Wars" }, "relationships": { "directors": { "data": { "id": "1", "type": "directors" } } } } } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 33
  34. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 34 Inclusion
  35. 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 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 35
  36. Inclusion Example for (Movie movie : pagedResult.getContent()) { jsonApiModelBuilder.included(movie.getDirectors()); }

    "included": [ { "id": "1", "type": "directors", "attributes": { "name": "Lana Wachowski" } }, … 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 36
  37. Sparse Fieldsets Convenient way to specify which  Attributes of

    Resources  Relationships (by name)  Attributes of included Relationships will be included in the JSON response 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 37
  38. 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<RepresentationModel<?>> findAll( @RequestParam(value = "included", required = false) String[] included, @RequestParam(value = "fields[movies]", required = false) String[] fieldsMovies) { 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 38
  39. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 39 Sparse Fieldsets Demo
  40. Meta  JSON:API Meta can be added using the builder

     Paging information Meta can be added automatically => Use PagedModel 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 40
  41. 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 } } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 41
  42. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 42 Configuration
  43. Configuration 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  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. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 43
  44. 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)); } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 44
  45. 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"))); 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 45
  46. Error Example { "errors": [ { "links": { "about": "http://movie-db.com/problem"

    }, "status": "400 BAD_REQUEST", "title": "Movie-based problem", "detail": "This is a test case" } ] } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 46
  47. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 47 Deserialization
  48. Deserialization  We want to deserialize JSON input directly to

    Spring HATEOAS representation models, e.g. for  HTTP POST  HTTP PATCH 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 48
  49. Deserialization Example (1) { "data": { "type": "movies", "attributes": {

    "title": "New Movie" }, "relationships": { "directors": { "data": [ { "id": "1", "type": "directors" }, { "id": "2", "type": "directors" } ] } } } } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 49
  50. Deserialization Example (2) public class MovieWithDirectors extends Movie { @JsonApiType

    String myType = "movies"; @JsonIgnore @JsonApiRelationships("directors") List<Director> directors; <= ONLY Director id will be set! } 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 50
  51. Polymorphic Deserialization (1) @PostMapping("/movies") public ResponseEntity<?> newMovie(@RequestBody EntityModel<Movie> movie) {

    … public class MovieWithRating extends Movie { private double rating; You could now annotate the Movie class with: @JsonTypeInfo(use = JsonTypeInfo.Id.NAME) @JsonSubTypes({ @JsonSubTypes.Type(value = MovieWithRating.class, name = "movieWithRating") }) 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 51
  52. Polymorphic Deserialization (2) { "data": { "id": "3", "type": "movies",

    "attributes": { "@type": "movieWithRating", "title": "Batman Begins", "rating": 8.2 } } } => would be deserialized to an object of class MovieWithRating, even though the controller method accepts the superclass Movie. Note: @-members will be introduced in JSON:API version 1.1. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 52
  53. 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution

    4.0 International License. 53 Discussion
  54. Links  Spring HATEOAS: https://github.com/spring-projects/spring-hateoas  JSON:API for Spring HATEOAS:

    https://github.com/toedter/spring-hateoas-jsonapi 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 54
  55. License  This work is licensed under a Creative Commons

    Attribution 4.0 International License.  See http://creativecommons.org/licenses/by/4.0/ 5/6/2021 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 55