2. Fundamentals

This section covers the basics of Spring HATEOAS and its fundamental domain abstractions.spring-doc.cn

The fundamental idea of hypermedia is to enrich the representation of a resource with hypermedia elements. The simplest form of that are links. They indicate a client that it can navigate to a certain resource. The semantics of a related resource are defined in a so-called link relation. You might have seen this in the header of an HTML file already:spring-doc.cn

Example 2. A link in an HTML document
<link href="theme.css" rel="stylesheet" type="text/css" />

As you can see the link points to a resource theme.css and indicates that it is a style sheet. Links often carry additional information, like the media type that the resource pointed to will return. However, the fundamental building blocks of a link are its reference and relation.spring-doc.cn

Spring HATEOAS lets you work with links through its immutable Link value type. Its constructor takes both a hypertext reference and a link relation, the latter being defaulted to the IANA link relation self. Read more on the latter in Link relations.spring-doc.cn

Example 3. Using links
Link link = Link.of("/something");
assertThat(link.getHref()).isEqualTo("/something");
assertThat(link.getRel()).isEqualTo(IanaLinkRelations.SELF);

link = Link.of("/something", "my-rel");
assertThat(link.getHref()).isEqualTo("/something");
assertThat(link.getRel()).isEqualTo(LinkRelation.of("my-rel"));

Link exposes other attributes as defined in RFC-8288. You can set them by calling the corresponding wither method on a Link instance.spring-doc.cn

Find more information on how to create links pointing to Spring MVC and Spring WebFlux controllers in Building links in Spring MVC and Building links in Spring WebFlux.spring-doc.cn

2.2. URI templates

For a Spring HATEOAS Link, the hypertext reference can not only be a URI, but also a URI template according to RFC-6570. A URI template contains so-called template variables and allows expansion of these parameters. This allows clients to turn parameterized templates into URIs without having to know about the structure of the final URI, it only needs to know about the names of the variables.spring-doc.cn

Example 4. Using links with templated URIs
Link link = Link.of("/{segment}/something{?parameter}");
assertThat(link.isTemplated()).isTrue(); (1)
assertThat(link.getVariableNames()).contains("segment", "parameter"); (2)

Map<String, Object> values = new HashMap<>();
values.put("segment", "path");
values.put("parameter", 42);

assertThat(link.expand(values).getHref()) (3)
    .isEqualTo("/path/something?parameter=42");
1 The Link instance indicates that is templated, i.e. it contains a URI template.
2 It exposes the parameters contained in the template.
3 It allows expansion of the parameters.

URI templates can be constructed manually and template variables added later on.spring-doc.cn

Example 5. Working with URI templates
UriTemplate template = UriTemplate.of("/{segment}/something")
  .with(new TemplateVariable("parameter", VariableType.REQUEST_PARAM);

assertThat(template.toString()).isEqualTo("/{segment}/something{?parameter}");

To indicate the relationship of the target resource to the current one so-called link relations are used. Spring HATEOAS provides a LinkRelation type to easily create String-based instances of it.spring-doc.cn

The Internet Assigned Numbers Authority contains a set of predefined link relations. They can be referred to via IanaLinkRelations.spring-doc.cn

Example 6. Using IANA link relations
Link link = Link.of("/some-resource"), IanaLinkRelations.NEXT);

assertThat(link.getRel()).isEqualTo(LinkRelation.of("next"));
assertThat(IanaLinkRelation.isIanaRel(link.getRel())).isTrue();

2.4. Representation models

To easily create hypermedia enriched representations, Spring HATEOAS provides a set of classes with RepresentationModel at their root. It’s basically a container for a collection of Links and has convenient methods to add those to the model. The models can later be rendered into various media type formats that will define how the hypermedia elements look in the representation. For more information on this, have a look at Media types.spring-doc.cn

Example 7. The RepresentationModel class hierarchy
diagram classes

The default way to work with a RepresentationModel is to create a subclass of it to contain all the properties the representation is supposed to contain, create instances of that class, populate the properties and enrich it with links.spring-doc.cn

Example 8. A sample representation model type
class PersonModel extends RepresentationModel<PersonModel> {

  String firstname, lastname;
}

The generic self-typing is necessary to let RepresentationModel.add(…) return instances of itself. The model type can now be used like this:spring-doc.cn

Example 9. Using the person representation model
PersonModel model = new PersonModel();
model.firstname = "Dave";
model.lastname = "Matthews";
model.add(Link.of("https://myhost/people/42"));

If you returned such an instance from a Spring MVC or WebFlux controller and the client sent an Accept header set to application/hal+json, the response would look as follows:spring-doc.cn

Example 10. The HAL representation generated for the person representation model
{
  "_links" : {
    "self" : {
      "href" : "https://myhost/people/42"
    }
  },
  "firstname" : "Dave",
  "lastname" : "Matthews"
}

2.4.1. Item resource representation model

For a resource that’s backed by a singular object or concept, a convenience EntityModel type exists. Instead of creating a custom model type for each concept, you can just reuse an already existing type and wrap instances of it into the EntityModel.spring-doc.cn

Example 11. Using EntityModel to wrap existing objects
Person person = new Person("Dave", "Matthews");
EntityModel<Person> model = EntityModel.of(person);

2.4.2. Collection resource representation model

For resources that are conceptually collections, a CollectionModel is available. Its elements can either be simple objects or RepresentationModel instances in turn.spring-doc.cn

Example 12. Using CollectionModel to wrap a collection of existing objects
Collection<Person> people = Collections.singleton(new Person("Dave", "Matthews"));
CollectionModel<Person> model = CollectionModel.of(people);

While an EntityModel is constrained to always contain a payload and thus allows to reason about the type arrangement on the sole instance, a CollectionModel's underlying collection can be empty. Due to Java’s type erasure, we cannot actually detect that a CollectionModel<Person> model = CollectionModel.empty() is actually a CollectionModel<Person> because all we see is the runtime instance and an empty collection. That missing type information can be added to the model by either adding it to the empty instance on construction via CollectionModel.empty(Person.class) or as fallback in case the underlying collection might be empty:spring-doc.cn

Iterable<Person> people = repository.findAll();
var model = CollectionModel.of(people).withFallbackType(Person.class);