Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

NPI – Spring Top – Temp – Non-Geo (Lightrun)

Get started with Spring 5 and Spring Boot 2, through the reference Learn Spring course:

NPI – Lightrun – Spring (partner)

We rely on other people’s code in our own work. Every day. It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.

The problem is, of course, when things fall apart in production - debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky. It’s difficult to understand what talks to what and, specifically, which part of the underlying library is at fault.

Lightrun is a new kind of debugger.

It's one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics. No hotfixes, redeployments, or restarts required.

Learn more in this quick, 5-minute Lightrun tutorial:

>> The Essential List of Spring Boot Annotations and Their Use Cases

1. Overview

This article will focus on the implementation of discoverability in a Spring REST Service and on satisfying the HATEOAS constraint.

This article focuses on Spring MVC. Our article An Intro to Spring HATEOAS describes how to use HATEOAS in Spring Boot.

2. Decoupling Discoverability Through Events

Discoverability as a separate aspect or concern of the web layer should be decoupled from the controller handling the HTTP request. For this purpose, the Controller will fire off events for all the actions that require additional manipulation of the response.

First, let's create the events:

public class SingleResourceRetrieved extends ApplicationEvent {
    private HttpServletResponse response;

    public SingleResourceRetrieved(Object source, HttpServletResponse response) {

        this.response = response;

    public HttpServletResponse getResponse() {
        return response;
public class ResourceCreated extends ApplicationEvent {
    private HttpServletResponse response;
    private long idOfNewResource;

    public ResourceCreated(Object source, 
      HttpServletResponse response, long idOfNewResource) {

        this.response = response;
        this.idOfNewResource = idOfNewResource;

    public HttpServletResponse getResponse() {
        return response;
    public long getIdOfNewResource() {
        return idOfNewResource;

Then, the Controller, with 2 simple operations – find by id and create:

@RequestMapping(value = "/foos")
public class FooController {

    private ApplicationEventPublisher eventPublisher;

    private IFooService service;

    @GetMapping(value = "foos/{id}")
    public Foo findById(@PathVariable("id") Long id, HttpServletResponse response) {
        Foo resourceById = Preconditions.checkNotNull(service.findOne(id));

        eventPublisher.publishEvent(new SingleResourceRetrieved(this, response));
        return resourceById;

    public void create(@RequestBody Foo resource, HttpServletResponse response) {
        Long newId = service.create(resource).getId();

        eventPublisher.publishEvent(new ResourceCreated(this, response, newId));

We can then handle these events with any number of decoupled listeners. Each of these can focus on its own particular case and help towards satisfying the overall HATEOAS constraint.

The listeners should be the last objects in the call stack and no direct access to them is necessary; as such they are not public.

3. Making the URI of a Newly Created Resource Discoverable

As discussed in the previous post on HATEOAS, the operation of creating a new Resource should return the URI of that resource in the Location HTTP header of the response.

We'll handle this using a listener:

class ResourceCreatedDiscoverabilityListener
  implements ApplicationListener<ResourceCreated>{

    public void onApplicationEvent(ResourceCreated resourceCreatedEvent){

       HttpServletResponse response = resourceCreatedEvent.getResponse();
       long idOfNewResource = resourceCreatedEvent.getIdOfNewResource();

       addLinkHeaderOnResourceCreation(response, idOfNewResource);
   void addLinkHeaderOnResourceCreation
     (HttpServletResponse response, long idOfNewResource){
       URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri().
       response.setHeader("Location", uri.toASCIIString());

In this example, we're making use of the ServletUriComponentsBuilder – which helps with using the current Request. This way, we don't need to pass anything around and we can simply access this statically.

If the API would return ResponseEntity – we could also use the Location support.

4. Getting a Single Resource

On retrieving a single Resource, the client should be able to discover the URI to get all Resources of that type:

class SingleResourceRetrievedDiscoverabilityListener
 implements ApplicationListener<SingleResourceRetrieved>{

    public void onApplicationEvent(SingleResourceRetrieved resourceRetrievedEvent){

        HttpServletResponse response = resourceRetrievedEvent.getResponse();
        addLinkHeaderOnSingleResourceRetrieval(request, response);
    void addLinkHeaderOnSingleResourceRetrieval(HttpServletResponse response){
        String requestURL = ServletUriComponentsBuilder.fromCurrentRequestUri().
        int positionOfLastSlash = requestURL.lastIndexOf("/");
        String uriForResourceCreation = requestURL.substring(0, positionOfLastSlash);

        String linkHeaderValue = LinkUtil
          .createLinkHeader(uriForResourceCreation, "collection");
        response.addHeader(LINK_HEADER, linkHeaderValue);

Note that the semantics of the link relation make use of the “collection” relation type, specified and used in several microformats, but not yet standardized.

The Link header is one of the most used HTTP headers for the purposes of discoverability. The utility to create this header is simple enough:

public class LinkUtil {
    public static String createLinkHeader(String uri, String rel) {
        return "<" + uri + ">; rel=\"" + rel + "\"";

5. Discoverability at the Root

The root is the entry point in the entire service – it's what the client comes into contact with when consuming the API for the first time.

If the HATEOAS constraint is to be considered and implemented throughout, then this is the place to start. Therefore all the main URIs of the system have to be discoverable from the root.

Let's now look at the controller for this:

@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void adminRoot(final HttpServletRequest request, final HttpServletResponse response) {
    String rootUri = request.getRequestURL().toString();

    URI fooUri = new UriTemplate("{rootUri}{resource}").expand(rootUri, "foos");
    String linkToFoos = LinkUtil.createLinkHeader(fooUri.toASCIIString(), "collection");
    response.addHeader("Link", linkToFoos);

This is, of course, an illustration of the concept, focusing on a single, sample URI, for Foo Resources. A real implementation should add, similarly, URIs for all the Resources published to the client.

5.1. Discoverability Is Not About Changing URIs

This can be a controversial point – on the one hand, the purpose of HATEOAS is to have the client discover the URIs of the API and not rely on hardcoded values. On the other hand – this is not how the web works: yes, URIs are discovered, but they are also bookmarked.

A subtle but important distinction is the evolution of the API – the old URIs should still work, but any client that will discover the API should discover the new URIs – which allows the API to change dynamically, and good clients to work well even when the API changes.

In conclusion – just because all URIs of the RESTful web service should be considered cool URIs (and cool URIs don't change) – that doesn't mean that adhering to the HATEOAS constraint isn't extremely useful when evolving the API.

6. Caveats of Discoverability

As some of the discussions around the previous articles state, the first goal of discoverability is to make minimal or no use of documentation and have the client learn and understand how to use the API via the responses it gets.

In fact, this shouldn’t be regarded as such a far fetched ideal – it's how we consume every new web page – without any documentation. So, if the concept is more problematic in the context of REST, then it must be a matter of technical implementation, not of a question of whether or not it’s possible.

That being said, technically, we are still far from a fully working solution – the specification and framework support are still evolving, and because of that, we have to make some compromises.

7. Conclusion

This article covered the implementation of some of the traits of discoverability in the context of a RESTful Service with Spring MVC and touched on the concept of discoverability at the root.

The implementation of all these examples and code snippets can be found over on GitHub – this is a Maven-based project, so it should be easy to import and run as it is.

Spring bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

REST bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course :

REST footer banner
Comments are closed on this article!