Hot questions for Spring HATEOAS

Top 10 Java Open Source / Spring / Spring HATEOAS

Question:

I have been looking at various examples of how to use Spring with REST. Our end target is a Spring HATEOAS/HAL setup

I have seen two distinct methods for rendering REST within Spring

  1. Via @RestController within a Controller

  2. Via @RepositoryRestResource within a Repository

The thing I am struggling to find is why would you use one over the other. When trying to implement HAL which is best?

Our database backend is Neo4j.


Answer:

Ok, so the short story is that you want to use the @RepositoryRestResource since this creates a HATEOAS service with Spring JPA.

As you can see here adding this annotation and linking it to your Pojo you have a fully functional HATEOAS service without having to implement the repository method or the REST service methods

If you add the @RestController then you have to implement each method that you want to expose on your own and also it does not export this to a HATEOAS format.

Question:

I'm using Spring 4.0.0.RELEASE, Spring Data Commons 1.7.0.M1, Spring Hateoas 0.8.0.RELEASE

My resource is a simple POJO:

public class UserResource extends ResourceSupport { ... }

My resource assembler converts User objects to UserResource objects:

@Component
public class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { 
    public UserResourceAssembler() {
        super(UserController.class, UserResource.class);
    }

    @Override
    public UserResource toResource(User entity) {
        // map User to UserResource
    }
}

Inside my UserController I want to retrieve Page<User> from my service and then convert it to PagedResources<UserResource> using PagedResourcesAssembler, like displayed here: https://stackoverflow.com/a/16794740/1321564

@RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(@PageableDefault Pageable p, PagedResourcesAssembler assembler) {
    Page<User> u = service.get(p)
    return assembler.toResource(u);
}

This doesn't call UserResourceAssembler and simply the contents of User are returned instead of my custom UserResource.

Returning a single resource works:

@Autowired
UserResourceAssembler assembler;

@RequestMapping(value="{id}", method=RequestMethod.GET)
UserResource getById(@PathVariable ObjectId id) throws NotFoundException {
    return assembler.toResource(service.getById(id));
}

The PagedResourcesAssembler wants some generic argument, but then I can't use T toResource(T), because I don't want to convert my Page<User> to PagedResources<User>, especially because User is a POJO and no Resource.

So the question is: How does it work?

EDIT:

My WebMvcConfigurationSupport:

@Configuration
@ComponentScan
@EnableHypermediaSupport
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(pageableResolver());
        argumentResolvers.add(sortResolver());
        argumentResolvers.add(pagedResourcesAssemblerArgumentResolver());
    }

    @Bean
    public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
        return new HateoasPageableHandlerMethodArgumentResolver(sortResolver());
    }

    @Bean
    public HateoasSortHandlerMethodArgumentResolver sortResolver() {
        return new HateoasSortHandlerMethodArgumentResolver();
    }

    @Bean
    public PagedResourcesAssembler<?> pagedResourcesAssembler() {
        return new PagedResourcesAssembler<Object>(pageableResolver(), null);
    }

    @Bean
    public PagedResourcesAssemblerArgumentResolver pagedResourcesAssemblerArgumentResolver() {
        return new PagedResourcesAssemblerArgumentResolver(pageableResolver(), null);
    }

    /* ... */
}
SOLUTION:
@Autowired
UserResourceAssembler assembler;

@RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(@PageableDefault Pageable p, PagedResourcesAssembler pagedAssembler) {
    Page<User> u = service.get(p)
    return pagedAssembler.toResource(u, assembler);
}

Answer:

You seem to have already found out about the proper way to use but I'd like to go into some of the details here a bit for others to find as well. I went into similar detail about PagedResourceAssembler in this answer.

Representation models

Spring HATEOAS ships with a variety of base classes for representation models that make it easy to create representations equipped with links. There are three types of classes provided out of the box:

  • Resource - an item resource. Effectively to wrap around some DTO or entity that captures a single item and enriches it with links.
  • Resources - a collection resource, that can be a collection of somethings but usually are a collection of Resource instances.
  • PagedResources - an extension of Resources that captures additional pagination information like the number of total pages etc.

All of these classes derive from ResourceSupport, which is a basic container for Link instances.

Resource assemblers

A ResourceAssembler is now the mitigating component to convert your domain objects or DTOs into such resource instances. The important part here is, that it turns one source object into one target object.

So the PagedResourcesAssembler will take a Spring Data Page instance and transform it into a PagedResources instance by evaluating the Page and creating the necessary PageMetadata as well as the prev and next links to navigate the pages. By default - and this is probably the interesting part here - it will use a plain SimplePagedResourceAssembler (an inner class of PRA) to transform the individual elements of the page into nested Resource instances.

To allow to customize this, PRA has additional toResource(…) methods that take a delegate ResourceAssembler to process the individual items. So you end up with something like this:

 class UserResource extends ResourceSupport { … }

 class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { … }

And the client code now looking something like this:

 PagedResourcesAssembler<User> parAssembler = … // obtain via DI
 UserResourceAssembler userResourceAssembler = … // obtain via DI

 Page<User> users = userRepository.findAll(new PageRequest(0, 10));

 // Tell PAR to use the user assembler for individual items.
 PagedResources<UserResource> pagedUserResource = parAssembler.toResource(
   users, userResourceAssembler);
Outlook

As of the upcoming Spring Data Commons 1.7 RC1 (and Spring HATEOAS 0.9 transitively) the prev and next links will be generated as RFC6540 compliant URI templates to expose the pagination request parameters configured in the HandlerMethodArgumentResolvers for Pageable and Sort.

The configuration you've shown above can be simplified by annotating the config class with @EnableSpringDataWebSupport which would let you get rid off all the explicit bean declarations.

Question:

Let's say I have two repositories:

@RepositoryRestResource(collectionResourceRel = "person", path = "person")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
    List<Person> findByLastName(@Param("name") String name);
}

and

@RepositoryRestResource(collectionResourceRel = "person1", path = "person1")
public interface PersonRepository1 extends PagingAndSortingRepository<Person1, Long> {
    List<Person1> findByLastName(@Param("name") String name);
}

with one regular controller:

@Controller
public class HelloController {
    @RequestMapping("/hello")
    @ResponseBody
    public HttpEntity<Hello> hello(@RequestParam(value = "name", required = false, defaultValue = "World") String name) {
        Hello hello = new Hello(String.format("Hello, %s!", name));
        hello.add(linkTo(methodOn(HelloController.class).hello(name)).withSelfRel());
        return new ResponseEntity<>(hello, HttpStatus.OK);
    }
}

Now, a response for http://localhost:8080/ is:

{
  "_links" : {
    "person" : {
      "href" : "http://localhost:8080/person{?page,size,sort}",
      "templated" : true
    },
    "person1" : {
      "href" : "http://localhost:8080/person1{?page,size,sort}",
      "templated" : true
    }
  }
}

but I want to get something like this:

{
  "_links" : {
    "person" : {
      "href" : "http://localhost:8080/person{?page,size,sort}",
      "templated" : true
    },
    "person1" : {
      "href" : "http://localhost:8080/person1{?page,size,sort}",
      "templated" : true
    },
    "hello" : {
      "href" : "http://localhost:8080/hello?name=World"
    }
  }
}

Answer:

@Component
public class HelloResourceProcessor implements ResourceProcessor<RepositoryLinksResource> {

    @Override
    public RepositoryLinksResource process(RepositoryLinksResource resource) {
        resource.add(ControllerLinkBuilder.linkTo(HelloController.class).withRel("hello"));
        return resource;
    }
}

based on

Question:

I have a data access layer made with Spring-Data. I'm now creating a web application on top of it. This one controller method should return a Spring-Data Page formatted as JSON.

Such a Page is a List with additional Paging info like total amount of records and so forth.

Is that possible and if yes how?

And directly related to that can I define the mapping of property names? Eg. meaning I would need define how the paging info properties are named in JSON (differently than in page). Is this possible and how?


Answer:

There's support for a scenario like this upcoming in Spring HATEOAS and Spring Data Commons. Spring HATEOAS comes with a PageMetadata object that essentially contains the same data as a Page but in a less enforcing manner, so that it can be more easily marshaled and unmarshaled.

Another aspect of the reason we implement this in combination with Spring HATEOAS and Spring Data commons is that there's little value in simply marshaling the page, it's content and the metadata but also want to generate the links to maybe existing next or previous pages, so that the client doesn't have to construct URIs to traverse these pages itself.

An example

Assume a domain class Person:

class Person {

  Long id;
  String firstname, lastname;
}

as well as it's corresponding repository:

interface PersonRepository extends PagingAndSortingRepository<Person, Long> { }

You can now expose a Spring MVC controller as follows:

@Controller
class PersonController {

  @Autowired PersonRepository repository;

  @RequestMapping(value = "/persons", method = RequestMethod.GET)
  HttpEntity<PagedResources<Person>> persons(Pageable pageable, 
    PagedResourcesAssembler assembler) {

    Page<Person> persons = repository.findAll(pageable);
    return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
  }
}

There's probably quite a bit to explain here. Let's take it step by step:

  1. We have a Spring MVC controller getting the repository wired into it. This requires Spring Data being set up (either through @Enable(Jpa|Mongo|Neo4j|Gemfire)Repositories or the XML equivalents). The controller method is mapped to /persons, which means it will accept all GET requests to that method.
  2. The core type returned from the method is a PagedResources - a type from Spring HATEOAS that represents some content enriched with Links plus a PageMetadata.
  3. When the method is invoked, Spring MVC will have to create instances for Pageable and PagedResourcesAssembler. To get this working you need to enable the Spring Data web support either through the @EnableSpringDataWebSupport annotation about to be introduced in the upcoming milestone of Spring Data Commons or via standalone bean definitions (documented here).

    The Pageable will be populated with information from the request. The default configuration will turn ?page=0&size=10 into a Pageable requesting the first page by a page size of 10.

    The PageableResourcesAssembler allows you to easily turn a Page into a PagedResources instances. It will not only add the page metadata to the response but also add the appropriate links to the representation based on what page you access and how your Pageable resolution is configured.

A sample JavaConfig configuration to enable this for JPA would look like this:

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
@EnableJpaRepositories
class ApplicationConfig {

  // declare infrastructure components like EntityManagerFactory etc. here
}
A sample request and response

Assume we have 30 Persons in the database. You can now trigger a request GET http://localhost:8080/persons and you'll see something similar to this:

{ "links" : [
    { "rel" : "next", "href" : "http://localhost:8080/persons?page=1&size=20 }
  ],
  "content" : [
    … // 20 Person instances rendered here
  ],
  "pageMetadata" : {
    "size" : 20,
    "totalElements" : 30,
    "totalPages" : 2,
    "number" : 0
  }
}

Note that the assembler produced the correct URI and also picks up the default configuration present to resolve the parameters into a Pageable for an upcoming request. This means, if you change that configuration, the links will automatically adhere to the change. By default the assembler points to the controller method it was invoked in but that can be customized by handing in a custom Link to be used as base to build the pagination links to overloads of the PagedResourcesAssembler.toResource(…) method.

Outlook

The PagedResourcesAssembler bits will be available in the upcoming milestone release of the Spring Data Babbage release train. It's already available in the current snapshots. You can see a working example of this in my Spring RESTBucks sample application. Simply clone it, run mvn jetty:run and curl http://localhost:8080/pages.

Question:

I'm using Spring Data REST, which supports HATEOAS. I'm new to this paradigm.

In GET responses from my RESTful web service I often receive results inside a node named _embedded.

I'm wondering: what is _embedded node for? Is it part of REST specification? Or part of HATEOAS specification? Or is it specific for the Spring implementation of them?

This is an example of JSON result for GET http://localhost:8080/mywebservice/features:

{
   "_links":
   {
       "search": { "href": "http://localhost:8080/mywebservice/features/search" }
   },
   "_embedded":
   {
       "features":
       [
           {
               "feature": "GROUND",
               "name": "Terreno",
               "data_type": "String",
               "_links":
               {
                   "self"  : { "href": "http://localhost:8080/mywebservice/features/GROUND" },
                   "values": { "href": "http://localhost:8080/mywebservice/features/GROUND }
               }
           },

           ...

       ]
   }
}

I noticed that I almost always have _embedded node in the response: if I request a collection, but even if a request a single resource by a search (for instance with GET http://localhost:8080/mywebservice/persons/search/findByEmail?email=example@example@.com).

I don't get _embedded node only if the request is for a specific resource, for instance when doing GET http://localhost:8080/mywebservice/features/GROUND.


Answer:

There's neither a REST nor a HATEOAS specification. Both are only concepts, or architectural styles, if you will. _embedded is part of the HAL format.

It's intended to embed (sic!) resources where otherwise only their URIs would be returned. For example GET http://localhost:8080/mywebservice/features is supposed to only return a list of URIs, like http://localhost:8080/mywebservice/features/GROUND, and you would have to load every single Feature yourself if you needed it. By utilizing _embedded all Feature resources get embedded into the response so you don't have to load them separately.

Question:

Question. How do I avoid n+1 queries with Spring Data REST?

Background. When querying Spring Data REST for a list of resources, each of the resulting top-level resources has links to the associated resources, as opposed to having the associated resources embedded directly in the top-level resources. For example, if I query for a list of data centers, the associated regions appear as links, like this:

{
  "links" : [ {
    "rel" : "self",
    "href" : "http://localhost:2112/api/datacenters/1"
  }, {
    "rel" : "datacenters.DataCenter.region",
    "href" : "http://localhost:2112/api/datacenters/1/region"
  } ],
  "name" : "US East 1a",
  "key" : "amazon-us-east-1a"
}

It is pretty typical, however, to want to get the associated information without having to do n+1 queries. To stick with the example above, I might want to display a list of data centers and their associated regions in a UI.

What I've tried. I created a custom query on my RegionRepository to get all the regions for a given set of data center keys:

@RestResource(path = "find-by-data-center-key-in")
Page<Region> findByDataCentersKeyIn(
    @Param("key") Collection<String> keys,
    Pageable pageable);

Unfortunately the links this query generates don't overlap with the links that the data center query above generates. Here are the links I get for the custom query:

http://localhost:2112/api/regions/search/find-by-data-center-key-in?key=amazon-us-east-1a&key=amazon-us-east-1b

{
  "links" : [ ],
  "content" : [ {
    "links" : [ {
      "rel" : "self",
      "href" : "http://localhost:2112/api/regions/1"
    }, {
      "rel" : "regions.Region.datacenters",
      "href" : "http://localhost:2112/api/regions/1/datacenters"
    }, {
      "rel" : "regions.Region.infrastructureprovider",
      "href" : "http://localhost:2112/api/regions/1/infrastructureprovider"
    } ],
    "name" : "US East (N. Virginia)",
    "key" : "amazon-us-east-1"
  }, {
    "links" : [ {
      "rel" : "self",
      "href" : "http://localhost:2112/api/regions/1"
    }, {
      "rel" : "regions.Region.datacenters",
      "href" : "http://localhost:2112/api/regions/1/datacenters"
    }, {
      "rel" : "regions.Region.infrastructureprovider",
      "href" : "http://localhost:2112/api/regions/1/infrastructureprovider"
    } ],
    "name" : "US East (N. Virginia)",
    "key" : "amazon-us-east-1"
  } ],
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 1
  }
}

The challenge seems to be that the data center query returns links that aren't particularly informative once you already understand the shape of the data. For example, I already know that the region for data center 1 is at /datacenters/1/region, so if I want actual information about which specific region is involved, I have to follow the link to get it. In particular I have to follow the link to get the canonical URI that shows up in the bulk queries that would allow me to avoid n+1 queries.


Answer:

The reason Spring Data REST works like this is the following: by default, we assume every application repository a primary resource of the REST service. Thus, if you expose a repository for an entity's related object you get links rendered to it and we expose the assignment of one entity to another via a nested resource (e.g. foo/{id}/bar).

To prevent this, annotate the related repository interface with @RestResource(exported = false) which prevents the entities managed by this repository from becoming top level resources.

The more general approach to this is starting with Spring Data REST letting you expose the resources you want to get managed and default rules applied. You can then customize the rendering and links by implementing ResourceProcessor<T> and registering your implementation as Spring bean. The ResourceProcessor will then allow you to customize the data rendered, links added to the representation etc.

For everything else, manually implement controllers (potentially blending into the URI space of the default controllers) and add links to those through ResourceProcessor implementations. An example for this can be seen in the Spring RESTBucks sample. The sample project uses Spring Data REST to manage Order instances and implements a custom controller to implement the more complex payment process. Beyond that it adds a link to the Order resource to point to the manually implemented code.

Question:

I am currently building an application with a REST interface, using Spring Boot, Hibernate and Spring-HATEOAS. My data model is defined as beans with @Entity annotation and I am using Spring's feature to automatically set up a Hibernate repository (Creating an interface extending PagingAndSortingRepository). My application is completely annotation-driven, i.e., I have no web.xml but configure everything with the Spring annotations like @Configuration, @Bean etc., and start the application from my main method with the help of SpringApplication.run(MyApp.class, args);

This works fine, but with this approach, a RepositoryRestHandlerMapping and EndpointHandlerMapping is created. These create a bunch of resources I neither need nor want. I implement my own controllers because they need to do more than the standard logic.

How can I prevent this default behaviour and disable these mappings?


Answer:

Exclude RepositoryRestMvcAutoConfiguration in your main class.

@EnableAutoConfiguration(exclude = RepositoryRestMvcAutoConfiguration.class)