Search results
Adding HAL links to Spring Boot 2 applications using Spring HATEOAS
1. OVERVIEW
HATEOAS, acronym for Hypermedia as the Engine of Application State, offers what your API consumers might do next when starting from a REST API entry point.
It includes hypermedia in the response, stateful links to related REST resources depending on business value or context. For instance, an upsell hypermedia link to upgrade to a Hotel suite instead of the room you might have in a shopping cart. A cancel hypermedia link to postpone a scheduled payment to a service provider.
This allows your API endpoints to reach the Level 3 of the famous Richardson Maturity Model. A more mature level than resources and verbs since it helps to provide API discoverability and self-documentation, to some degree.
This blog post covers the configuration and implementation details to include HAL representations in your API responses using Spring Boot 2.1.x.RELEASE and Spring HATEOAS 0.25.x.RELEASE.
2. REQUIREMENTS
- Java 8+.
- Maven 3.2+.
- Familiarity with Spring Framework.
3. HATEOAS IMPLEMENTATION
This section helps you to include HAL (Hypertext Application Language) hyperlinks in REST response payloads. From dependencies, properties settings, to bean definitions, controller implementation, domain and response models, and mappers.
3.1. DEPENDENCIES
pom.xml
:
This is the only dependency required when used in Spring Boot applications, which brings in spring-boot-starter-web
. Other dependencies found in this file provide support for the demo application to compile and run.
3.2. PROPERTIES CONFIGURATION
application.yml
:
Or further more, Refreshing application configuration using Spring Cloud Config Server, Spring Cloud Bus, RabbitMQ and Git without bouncing the service.
spring.jackson.default-property-inclusion
set to NON_ABSENT
configures Jackson’s ObjectMapper to leave null or in the case of Java 8+, empty Optional attributes, out of the JSON response payload. It’s shortcut to setting @JsonInclude(Include.NON_ABSENT) to every REST resource class.
spring.hateoas.use-hal-as-default-json-media-type
set to false prevents sending back the Content-Type
header set to application/hal+json
by default.
application/vnd.asimio-v1+json
payloads and application/json+hal
would play along. Stay tuned, I might cover this functionality in a future post.
3.3. REST CONTROLLER
Let’s analyze ActorController.java
endpoint implementations independently.
- retrieveActor()
The endpoints implemented in this class produces application/json
or application/hal+json
unless a particular implementation specifies a different value.
The retrieveActor() method uses MapStruct-based ActorResourceMapper.java to not only map from the JPA domain entity to the REST resource, but to also add HATEOAS links.
- retrieveActorFilms()
Similarly to retrieveActor() method, retrieveActorFilms() also uses a MapStruct mapper to map from the JPA entity to the endpoint response.
The difference is executing addLinks()
method in the Controller class to add HAL instead of adding the links via the JPA domain model to RESTful resource model mapper.
The REST response would include these links:
Just two different ways to accomplish the same goal: to add HAL support to REST APIs responses.
3.4. JPA AND REST RESOURCE MODELS
Actor.java
:
Actor is a JPA entity, part of the application’s internal domain model. I would suggest to never expose the model directly as the API response. Any change to the internal domain model might cause breaking changes to the contracted API response and thus affecting your APIs consumers.
This JPA entity also implements Spring HATEOAS’s Identifiable marker interface used to identify objects by their id
to include it in the links.
Spring HATEOAS executes the getId()
method via statements like:
ControllerLinkBuilder.linkTo(ActorController.class).slash(entity);
as found in ActorResourceMapper.java; to generate hypermedia link:
/api/actors/1
.
ActorResource.java
:
ActorResource is a RESTful API response. This is what your RESTful endpoint will be sending back to your consumers.
Domain model changes should have minimum impact to the API responses to prevent breaking your API clients. In case you have to make a breaking change, I would suggest to version the endpoints.
Or further more, Discovering multiple API versions using Eureka and Ribbon.
This resource model extends ResourceSupport to provide support for retrieving, adding, removing links via add(Link... links)
and others methods, as included in ActorResourceMapper.java.
The @Relation configures the way Spring HATEOAS includes instances of this class in the response payload. In this case it will use actors
as the name for a collection of ActorResource instead of defaulting to actorList
.
3.5. RESOURCE MAPPER
ActorResourceMapper.java
:
This is a MapStruct-based mapper that maps the internal domain model Actor to the API response ActorResource.
Stay tuned, I might cover Java objects mapping using MapStruct in a future post.
The addLinks()
method adds two hypermedia links to the API response which would look like:
FilmResourceMapper.java
:
This mapper adds a self
link to the film
resource requested and also adds a link to each individual actor
resource that performed in the film
.
The REST response would include these links:
ResourceMapper.java
:
This is a Functional Interface that other mappers implements to take advantage of the default method implementation. It maps a collection of entities to REST resources sent as the API response.
3.6. X-FORWARDED-* HEADERS
If your RESTful Web services are behind a Gateway or Edge server like Zuul or front-end proxy, you might want to replace the host, port, protocol, etc. with those of the Edge server.
spring-web dependency allows you to accomplish this behavior using ForwardedHeaderFilter Servlet Filter:
WebConfig.java
:
This ForwardedHeaderFilter bean definition is all you need to render absolute links using X-Forwarded-* headers set by the fronting load balancer.
4. SAMPLE RESPONSE PAYLOADS
- A sample
application/json
, the JSON with hypermedia links response payload looks like:
Notice the links include one "rel": "self"
and many "rel": "actors"
entries.
- While the same request but accepting
application/hal+json
, the JSON + HAL response looks like:
Notice the _links object. That’s the name specified in the HAL specification for hal+json
payloads.
Also notice the actors array. The default behavior is to named it actorList but that was configured in ActorResource via the @Relation annotation.
- A third example, also accepting
application/hal+json
, the JSON + HAL response looks like:
It now includes the _embedded object.
- Finally a sample response sending X-Forwarded-* headers:
The request sends X-Forwarded-Host
and X-Forwarded-Port
headers replacing the host and port the RESTful application listens on with an Edge server the consuming applications should send the requests to.
5. CONCLUSION
Adding HAL representations to your REST API responses suggest your consumers what they might do next. They provide clients with some kind of statefulness. Although the links don’t specify if it’s a GET, a DELETE or a PUT request.
A REST response might include a link to upgrade from a standard Hotel room to a suite, a link to upgrade to a first-class airline seat, to add related items to an existing shopping cart, etc.
There are a number of libraries that help you to include hypermedia in your RESTful responses. If you are invested in Spring Boot, the spring-boot-starter-hateoas
starter simplifies such a task.
This blog post helps you with the configuration and implementation details to get started including HAL hypermedia in your API responses. It doesn’t cover pagination but stay tuned, I’ll follow-up with another blog post that adds HAL pagination support to Spring Boot applications using Spring HATEOAS.
Thanks for reading and as always, feedback is very much appreciated. If you found this post helpful and would like to receive updates when content like this gets published, sign up to the newsletter.
6. SOURCE CODE
Accompanying source code for this blog post can be found at:
7. REFERENCES
- https://docs.spring.io/spring-hateoas/docs/current/reference/html/
- https://martinfowler.com/articles/richardsonMaturityModel.html
- https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html
- https://docs.spring.io/spring-hateoas/docs/0.25.1.RELEASE/org/springframework/hateoas/ResourceSupport.html
- http://stateless.co/hal_specification.html