3. Server-side support

Now we have the domain vocabulary in place, but the main challenge remains: how to create the actual URIs to be wrapped into Link instances in a less fragile way. Right now, we would have to duplicate URI strings all over the place. Doing so is brittle and unmaintainable.spring-doc.cn

Assume you have your Spring MVC controllers implemented as follows:spring-doc.cn

@Controller
class PersonController {

  @GetMapping("/people")
  HttpEntity<PersonModel> showAll() { … }

  @GetMapping(value = "/{person}", method = RequestMethod.GET)
  HttpEntity<PersonModel> show(@PathVariable Long person) { … }
}

We see two conventions here. The first is a collection resource that is exposed through @GetMapping annotation of the controller method, with individual elements of that collection exposed as direct sub resources. The collection resource might be exposed at a simple URI (as just shown) or more complex ones (such as /people/{id}/addresses). Suppose you would like to link to the collection resource of all people. Following the approach from above would cause two problems:spring-doc.cn

  • To create an absolute URI, you would need to look up the protocol, hostname, port, servlet base, and other values. This is cumbersome and requires ugly manual string concatenation code.spring-doc.cn

  • You probably do not want to concatenate the /people on top of your base URI, because you would then have to maintain the information in multiple places. If you change the mapping, you then have to change all the clients pointing to it.spring-doc.cn

Spring HATEOAS now provides a WebMvcLinkBuilder that lets you create links by pointing to controller classes. The following example shows how to do so:spring-doc.cn

import static org.sfw.hateoas.server.mvc.WebMvcLinkBuilder.*;

Link link = linkTo(PersonController.class).withRel("people");

assertThat(link.getRel()).isEqualTo(LinkRelation.of("people"));
assertThat(link.getHref()).endsWith("/people");

The WebMvcLinkBuilder uses Spring’s ServletUriComponentsBuilder under the hood to obtain the basic URI information from the current request. Assuming your application runs at localhost:8080/your-app, this is exactly the URI on top of which you are constructing additional parts. The builder now inspects the given controller class for its root mapping and thus ends up with localhost:8080/your-app/people. You can also build more nested links as well. The following example shows how to do so:spring-doc.cn

Person person = new Person(1L, "Dave", "Matthews");
//                 /person                 /     1
Link link = linkTo(PersonController.class).slash(person.getId()).withSelfRel();
assertThat(link.getRel(), is(IanaLinkRelation.SELF.value()));
assertThat(link.getHref(), endsWith("/people/1"));

The builder also allows creating URI instances to build up (for example, response header values):spring-doc.cn

HttpHeaders headers = new HttpHeaders();
headers.setLocation(linkTo(PersonController.class).slash(person).toUri());

return new ResponseEntity<PersonModel>(headers, HttpStatus.CREATED);

You can even build links that point to methods or create dummy controller method invocations. The first approach is to hand a Method instance to the WebMvcLinkBuilder. The following example shows how to do so:spring-doc.cn

Method method = PersonController.class.getMethod("show", Long.class);
Link link = linkTo(method, 2L).withSelfRel();

assertThat(link.getHref()).endsWith("/people/2"));

This is still a bit dissatisfying, as we have to first get a Method instance, which throws an exception and is generally quite cumbersome. At least we do not repeat the mapping. An even better approach is to have a dummy method invocation of the target method on a controller proxy, which we can create by using the methodOn(…) helper. The following example shows how to do so:spring-doc.cn

Link link = linkTo(methodOn(PersonController.class).show(2L)).withSelfRel();

assertThat(link.getHref()).endsWith("/people/2");

methodOn(…) creates a proxy of the controller class that records the method invocation and exposes it in a proxy created for the return type of the method. This allows the fluent expression of the method for which we want to obtain the mapping. However, there are a few constraints on the methods that can be obtained by using this technique:spring-doc.cn

  • The return type has to be capable of proxying, as we need to expose the method invocation on it.spring-doc.cn

  • The parameters handed into the methods are generally neglected (except the ones referred to through @PathVariable, because they make up the URI).spring-doc.cn

Collection-valued request parameters can actually be materialized in two different ways. The URI template specification lists the composite way of rendering them that repeats the parameter name for each value (param=value1&param=value2), and the non-composite one that separates values by a comma (param=value1,value2). Spring MVC properly parses the collection out of both formats. Rendering the values defaults to the composite style by default. If you want the values to be rendered in the non-composite style, you can use the @NonComposite annotation with the request parameter handler method parameter:spring-doc.cn

@Controller
class PersonController {

  @GetMapping("/people")
  HttpEntity<PersonModel> showAll(
    @NonComposite @RequestParam Collection<String> names) { … } (1)
}

var values = List.of("Matthews", "Beauford");
var link = linkTo(methodOn(PersonController.class).showAll(values)).withSelfRel(); (2)

assertThat(link.getHref()).endsWith("/people?names=Matthews,Beauford"); (3)
1 We use the @NonComposite annotation to declare we want values to be rendered comma-separated.
2 We invoke the method using a list of values.
3 See how the request parameter is rendered in the expected format.
The reason we’re exposing @NonComposite is that the composite way of rendering request parameters is baked into the internals of Spring’s UriComponents builder and we only introduced that non-composite style in Spring HATEOAS 1.4. If we started from scratch today, we’d probably default to that style and rather let users opt into the composite style explicitly rather than the other way around.

3.3. Affordances

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.spring-doc.cn

— James J. Gibson
The Ecological Approach to Visual Perception (page 126)

REST-based resources provide not just data but controls. The last ingredient to form a flexible service are detailed affordances on how to use the various controls. Because affordances are associated with links, Spring HATEOAS provides an API to attach as many related methods as needed to a link. Just as you can create links by pointing to Spring MVC controller methods (see Building links in Spring MVC for details) you …​spring-doc.cn

The following code shows how to take a self link and associate two more affordances:spring-doc.cn

Example 13. Connecting affordances to GET /employees/{id}
@GetMapping("/employees/{id}")
public EntityModel<Employee> findOne(@PathVariable Integer id) {

  Class<EmployeeController> controllerClass = EmployeeController.class;

  // Start the affordance with the "self" link, i.e. this method.
  Link findOneLink = linkTo(methodOn(controllerClass).findOne(id)).withSelfRel(); (1)

  // Return the affordance + a link back to the entire collection resource.
  return EntityModel.of(EMPLOYEES.get(id), //
      findOneLink //
          .andAffordance(afford(methodOn(controllerClass).updateEmployee(null, id))) (2)
          .andAffordance(afford(methodOn(controllerClass).partiallyUpdateEmployee(null, id)))); (3)
}
1 Create the self link.
2 Associate the updateEmployee method with the self link.
3 Associate the partiallyUpdateEmployee method with the self link.

Using .andAffordance(afford(…​)), you can use the controller’s methods to connect a PUT and a PATCH operation to a GET operation. Imagine that the related methods afforded above look like this:spring-doc.cn

Example 14. updateEmpoyee method that responds to PUT /employees/{id}
@PutMapping("/employees/{id}")
public ResponseEntity<?> updateEmployee( //
    @RequestBody EntityModel<Employee> employee, @PathVariable Integer id)
Example 15. partiallyUpdateEmployee method that responds to PATCH /employees/{id}
@PatchMapping("/employees/{id}")
public ResponseEntity<?> partiallyUpdateEmployee( //
    @RequestBody EntityModel<Employee> employee, @PathVariable Integer id)

Pointing to those methods using the afford(…) methods will cause Spring HATEOAS to analyze the request body and response types and capture metadata to allow different media type implementations to use that information to translate that into descriptions of the input and outputs.spring-doc.cn

3.3.1. Building affordances manually

While the primary way to register affordances for a link, it might be necessary to build some of them manually. This can be achieved by using the Affordances API:spring-doc.cn

Example 16. Using the Affordances API to manually register affordances
var methodInvocation = methodOn(EmployeeController.class).all();

var link = Affordances.of(linkTo(methodInvocation).withSelfRel()) (1)

    .afford(HttpMethod.POST) (2)
    .withInputAndOutput(Employee.class) //
    .withName("createEmployee") //

    .andAfford(HttpMethod.GET) (3)
    .withOutput(Employee.class) //
    .addParameters(//
        QueryParameter.optional("name"), //
        QueryParameter.optional("role")) //
    .withName("search") //

    .toLink();
1 You start by creating an instance of Affordances from a Link instance creating the context for describing the affordances.
2 Each affordance starts with the HTTP method it’s supposed to support. We then register a type as payload description and name the affordance explicitly. The latter can be omitted and a default name will be derived from the HTTP method and input type name. This effectively creates the same affordance as the pointer to EmployeeController.newEmployee(…) created.
3 The next affordance is built to reflect what’s happening for the pointer to EmployeeController.search(…). Here we define Employee to be the model for the response created and explicitly register QueryParameters.

Affordances are backed by media type specific affordance models that translate the general affordance metadata into specific representations. Please make sure to check the section on affordances in the Media types section to find more details about how to control the exposure of that metadata.spring-doc.cn

RFC-7239 forwarding headers are most commonly used when your application is behind a proxy, behind a load balancer, or in the cloud. The node that actually receives the web request is part of the infrastructure, and forwards the request to your application.spring-doc.cn

Your application may be running on localhost:8080, but to the outside world you’re expected to be at reallycoolsite.com (and on the web’s standard port 80). By having the proxy include extra headers (which many already do), Spring HATEOAS can generate links properly as it uses Spring Framework functionality to obtain the base URI of the original request.spring-doc.cn

Anything that can change the root URI based on external inputs must be properly guarded. That’s why, by default, forwarded header handling is disabled. You MUST enable it to be operational. If you are deploying to the cloud or into a configuration where you control the proxies and load balancers, then you’ll certainly want to use this feature.

To enable forwarded header handling you need to register Spring’s ForwardedHeaderFilter for Spring MVC (details here) or ForwardedHeaderTransformer for Spring WebFlux (details here) in your application. In a Spring Boot application those components can be simply declared as Spring beans as described here.spring-doc.cn

Example 17. Registering a ForwardedHeaderFilter
@Bean
ForwardedHeaderFilter forwardedHeaderFilter() {
    return new ForwardedHeaderFilter();
}

This will create a servlet filter that processes all the X-Forwarded-… headers. And it will register it properly with the servlet handlers.spring-doc.cn

For a Spring WebFlux application, the reactive counterpart is ForwardedHeaderTransformer:spring-doc.cn

Example 18. Registering a ForwardedHeaderTransformer
@Bean
ForwardedHeaderTransformer forwardedHeaderTransformer() {
    return new ForwardedHeaderTransformer();
}

This will create a function that transforms reactive web requests, processing X-Forwarded-… headers. And it will register it properly with WebFlux.spring-doc.cn

With configuration as shown above in place, a request passing X-Forwarded-… headers will see those reflected in the links generated:spring-doc.cn

Example 19. A request using X-Forwarded-… headers
curl -v localhost:8080/employees \
    -H 'X-Forwarded-Proto: https' \
    -H 'X-Forwarded-Host: example.com' \
    -H 'X-Forwarded-Port: 9001'
Example 20. The corresponding response with the links generated to consider those headers
{
  "_embedded": {
    "employees": [
      {
        "id": 1,
        "name": "Bilbo Baggins",
        "role": "burglar",
        "_links": {
          "self": {
            "href": "https://example.com:9001/employees/1"
          },
          "employees": {
            "href": "https://example.com:9001/employees"
          }
        }
      }
    ]
  },
  "_links": {
    "self": {
      "href": "https://example.com:9001/employees"
    },
    "root": {
      "href": "https://example.com:9001"
    }
  }
}
EntityLinks and its various implementations are NOT currently provided out-of-the-box for Spring WebFlux applications. The contract defined in the EntityLinks SPI was originally aimed at Spring Web MVC and doesn’t consider Reactor types. Developing a comparable contract that supports reactive programming is still in progress.

So far, we have created links by pointing to the web framework implementations (that is, the Spring MVC controllers) and inspected the mapping. In many cases, these classes essentially read and write representations backed by a model class.spring-doc.cn

The EntityLinks interface now exposes an API to look up a Link or LinkBuilder based on the model types. The methods essentially return links that point either to the collection resource (such as /people) or to an item resource (such as /people/1). The following example shows how to use EntityLinks:spring-doc.cn

EntityLinks links = …;
LinkBuilder builder = links.linkFor(Customer.class);
Link link = links.linkToItemResource(Customer.class, 1L);

EntityLinks is available via dependency injection by activating @EnableHypermediaSupport in your Spring MVC configuration. This will cause a variety of default implementations of EntityLinks being registered. The most fundamental one is ControllerEntityLinks that inspects SpringMVC controller classes. If you want to register your own implementation of EntityLinks, check out this section.spring-doc.cn

3.5.1. EntityLinks based on Spring MVC controllers

Activating entity links functionality causes all the Spring MVC controllers available in the current ApplicationContext to be inspected for the @ExposesResourceFor(…) annotation. The annotation exposes which model type the controller manages. Beyond that, we assume that you adhere to the following URI mapping setup and conventions:spring-doc.cn

  • A type level @ExposesResourceFor(…) declaring which entity type the controller exposes collection and item resources for.spring-doc.cn

  • A class level base mapping that represents the collection resource.spring-doc.cn

  • An additional method level mapping that extends the mapping to append an identifier as additional path segment.spring-doc.cn

The following example shows an implementation of an EntityLinks-capable controller:spring-doc.cn

@Controller
@ExposesResourceFor(Order.class) (1)
@RequestMapping("/orders") (2)
class OrderController {

  @GetMapping (3)
  ResponseEntity orders(…) { … }

  @GetMapping("{id}") (4)
  ResponseEntity order(@PathVariable("id") … ) { … }
}
1 The controller indicates it’s exposing collection and item resources for the entity Order.
2 Its collection resource is exposed under /orders
3 That collection resource can handle GET requests. Add more methods for other HTTP methods at your convenience.
4 An additional controller method to handle a subordinate resource taking a path variable to expose an item resource, i.e. a single Order.

With this in place, when you enable EntityLinks @EnableHypermediaSupport in your Spring MVC configuration, you can create links to the controller as follows:spring-doc.cn

@Controller
class PaymentController {

  private final EntityLinks entityLinks;

  PaymentController(EntityLinks entityLinks) { (1)
    this.entityLinks = entityLinks;
  }

  @PutMapping(…)
  ResponseEntity payment(@PathVariable Long orderId) {

    Link link = entityLinks.linkToItemResource(Order.class, orderId); (2)
    …
  }
}
1 Inject EntityLinks made available by @EnableHypermediaSupport in your configuration.
2 Use the APIs to build links by using the entity types instead of controller classes.

As you can see, you can refer to resources managing Order instances without referring to OrderController explicitly.spring-doc.cn

3.5.2. EntityLinks API in detail

Fundamentally, EntityLinks allows to build LinkBuilders and Link instances to collection and item resources of an entity type. Methods starting with linkFor… will produce LinkBuilder instances for you to extend and augment with additional path segments, parameters, etc. Methods starting with linkTo produce fully prepared Link instances.spring-doc.cn

While for collection resources providing an entity type is sufficient, links to item resources will need an identifier provided. This usually looks like this:spring-doc.cn

Example 21. Obtaining a link to an item resource
entityLinks.linkToItemResource(order, order.getId());

If you find yourself repeating those method calls the identifier extraction step can be pulled out into a reusable Function to be reused throughout different invocations:spring-doc.cn

Function<Order, Object> idExtractor = Order::getId; (1)

entityLinks.linkToItemResource(order, idExtractor); (2)
1 The identifier extraction is externalized so that it can be held in a field or constant.
2 The link lookup using the extractor.
TypedEntityLinks

As controller implementations are often grouped around entity types, you’ll very often find yourself using the same extractor function (see EntityLinks API in detail for details) all over the controller class. We can centralize the identifier extraction logic even more by obtaining a TypedEntityLinks instance providing the extractor once, so that the actual lookups don’t have to deal with the extraction anymore at all.spring-doc.cn

Example 22. Using TypedEntityLinks
class OrderController {

  private final TypedEntityLinks<Order> links;

  OrderController(EntityLinks entityLinks) { (1)
    this.links = entityLinks.forType(Order::getId); (2)
  }

  @GetMapping
  ResponseEntity<Order> someMethod(…) {

    Order order = … // lookup order

    Link link = links.linkToItemResource(order); (3)
  }
}
1 Inject an EntityLinks instance.
2 Indicate you’re going to look up Order instances with a certain identifier extractor function.
3 Look up item resource links based on a sole Order instance.

3.5.3. EntityLinks as SPI

The EntityLinks instance created by @EnableHypermediaSupport is of type DelegatingEntityLinks which will in turn pick up all other EntityLinks implementations available as beans in the ApplicationContext. It’s registered as primary bean so that it’s always the sole injection candidate when you inject EntityLinks in general. ControllerEntityLinks is the default implementation that will be included in the setup, but users are free to implement and register their own implementations. Making those available to the EntityLinks instance available for injection is a matter of registering your implementation as Spring bean.spring-doc.cn

Example 23. Declaring a custom EntityLinks implementation
@Configuration
class CustomEntityLinksConfiguration {

  @Bean
  MyEntityLinks myEntityLinks(…) {
    return new MyEntityLinks(…);
  }
}

An example for the extensibility of this mechanism is Spring Data REST’s RepositoryEntityLinks, which uses the repository mapping information to create links pointing to resources backed by Spring Data repositories. At the same time, it even exposes additional lookup methods for other types of resources. If you want to make use of these, simply inject RepositoryEntityLinks explicitly.spring-doc.cn

3.6. Representation model assembler

As the mapping from an entity to a representation model must be used in multiple places, it makes sense to create a dedicated class responsible for doing so. The conversion contains very custom steps but also a few boilerplate steps:spring-doc.cn

  1. Instantiation of the model classspring-doc.cn

  2. Adding a link with a rel of self pointing to the resource that gets rendered.spring-doc.cn

Spring HATEOAS now provides a RepresentationModelAssemblerSupport base class that helps reduce the amount of code you need to write. The following example shows how to use it:spring-doc.cn

class PersonModelAssembler extends RepresentationModelAssemblerSupport<Person, PersonModel> {

  public PersonModelAssembler() {
    super(PersonController.class, PersonModel.class);
  }

  @Override
  public PersonModel toModel(Person person) {

    PersonModel resource = createResource(person);
    // … do further mapping
    return resource;
  }
}
createResource(…​) is code you write to instantiate a PersonModel object given a Person object. It should only focus on setting attributes, not populating Links.

Setting the class up as we did in the preceding example gives you the following benefits:spring-doc.cn

  • There are a handful of createModelWithId(…) methods that let you create an instance of the resource and have a Link with a rel of self added to it. The href of that link is determined by the configured controller’s request mapping plus the ID of the entity (for example, /people/1).spring-doc.cn

  • The resource type gets instantiated by reflection and expects a no-arg constructor. If you want to use a dedicated constructor or avoid the reflection performance overhead, you can override instantiateModel(…).spring-doc.cn

You can then use the assembler to either assemble a RepresentationModel or a CollectionModel. The following example creates a CollectionModel of PersonModel instances:spring-doc.cn

Person person = new Person(…);
Iterable<Person> people = Collections.singletonList(person);

PersonModelAssembler assembler = new PersonModelAssembler();
PersonModel model = assembler.toModel(person);
CollectionModel<PersonModel> model = assembler.toCollectionModel(people);

3.7. Representation Model Processors

Sometimes you need to tweak and adjust hypermedia representations after they have been assembled.spring-doc.cn

A perfect example is when you have a controller that deals with order fulfillment, but you need to add links related to making payments.spring-doc.cn

Imagine having your ordering system producing this type of hypermedia:spring-doc.cn

{
  "orderId" : "42",
  "state" : "AWAITING_PAYMENT",
  "_links" : {
    "self" : {
      "href" : "http://localhost/orders/999"
    }
  }
}

You wish to add a link so the client can make payment, but don’t want to mix details about your PaymentController into the OrderController. Instead of polluting the details of your ordering system, you can write a RepresentationModelProcessor like this:spring-doc.cn

public class PaymentProcessor implements RepresentationModelProcessor<EntityModel<Order>> { (1)

  @Override
  public EntityModel<Order> process(EntityModel<Order> model) {

    model.add( (2)
        Link.of("/payments/{orderId}").withRel(LinkRelation.of("payments")) //
            .expand(model.getContent().getOrderId()));

    return model; (3)
  }
}
1 This processor will only be applied to EntityModel<Order> objects.
2 Manipulate the existing EntityModel object by adding an unconditional link.
3 Return the EntityModel so it can be serialized into the requested media type.

Register the processor with your application:spring-doc.cn

@Configuration
public class PaymentProcessingApp {

  @Bean
  PaymentProcessor paymentProcessor() {
    return new PaymentProcessor();
  }
}

Now when you issue a hypermedia respresentation of an Order, the client receives this:spring-doc.cn

{
  "orderId" : "42",
  "state" : "AWAITING_PAYMENT",
  "_links" : {
    "self" : {
      "href" : "http://localhost/orders/999"
    },
    "payments" : { (1)
      "href" : "/payments/42" (2)
    }
  }
}
1 You see the LinkRelation.of("payments") plugged in as this link’s relation.
2 The URI was provided by the processor.

This example is quite simple, but you can easily:spring-doc.cn

  • Use WebMvcLinkBuilder or WebFluxLinkBuilder to construct a dynamic link to your PaymentController.spring-doc.cn

  • Inject any services needed to conditionally add other links (e.g. cancel, amend) that are driven by state.spring-doc.cn

  • Leverage cross cutting services like Spring Security to add, remove, or revise links based upon the current user’s context.spring-doc.cn

Also, in this example, the PaymentProcessor alters the provided EntityModel<Order>. You also have the power to replace it with another object. Just be advised the API requires the return type to equal the input type.spring-doc.cn

3.7.1. Processing empty collection models

To find the right set of RepresentationModelProcessor instance to invoke for a RepresentationModel instance, the invoking infrastructure performs a detailed analysis of the generics declaration of the RepresentationModelProcessors registered. For CollectionModel instances, this includes inspecting the elements of the underlying collection, as at runtime, the sole model instance does not expose generics information (due to Java’s type erasure). That means, by default, RepresentationModelProcessor instances are not invoked for empty collection models. To still allow the infrastructure to deduce the payload types correctly, you can initialize empty CollectionModel instances with an explicit fallback payload type right from the start, or register it by calling CollectionModel.withFallbackType(…). See Collection resource representation model for details.spring-doc.cn

3.8. Using the LinkRelationProvider API

When building links, you usually need to determine the relation type to be used for the link. In most cases, the relation type is directly associated with a (domain) type. We encapsulate the detailed algorithm to look up the relation types behind a LinkRelationProvider API that lets you determine the relation types for single and collection resources. The algorithm for looking up the relation type follows:spring-doc.cn

  1. If the type is annotated with @Relation, we use the values configured in the annotation.spring-doc.cn

  2. If not, we default to the uncapitalized simple class name plus an appended List for the collection rel.spring-doc.cn

  3. If the EVO inflector JAR is in the classpath, we use the plural of the single resource rel provided by the pluralizing algorithm.spring-doc.cn

  4. @Controller classes annotated with @ExposesResourceFor (see Using the EntityLinks interface for details) transparently look up the relation types for the type configured in the annotation, so that you can use LinkRelationProvider.getItemResourceRelFor(MyController.class) and get the relation type of the domain type exposed.spring-doc.cn

A LinkRelationProvider is automatically exposed as a Spring bean when you use @EnableHypermediaSupport. You can plug in custom providers by implementing the interface and exposing them as Spring beans in turn.spring-doc.cn