Search code examples
javaspring-bootspring-hateoashypermedia

How to get a response in HAL-Format with Spring-Hateoas


Basically i have the same issue like the member who posted this question

When i request a single user in my Application, i get the response in the HAL-format, like i wish

http://localhost:8080/api/v1/users/25 with GET:

{
"userId": "25",
"firstname": "Beytullah",
"lastname": "Güneyli",
"username": "gueneylb",
"_links": {
"self": {
  "href": "http://localhost:8080/api/v1/users/25"
 },
"roles": [
  {
    "href": "http://localhost:8080/api/v1/roles/33"
  },
  {
    "href": "http://localhost:8080/api/v1/roles/34"
  }
 ]
 }
 }

But, when i request all users , i get the response in non-HAL-Format, like this:

http://localhost:8080/api/v1/users with GET:

[...

{
"userId": "25",
"firstname": "Beytullah",
"lastname": "Güneyli",
"username": "gueneylb",
"links": [
  {
    "rel": "self",
    "href": "http://localhost:8080/api/v1/users/25"
  },
  {
    "rel": "roles",
    "href": "http://localhost:8080/api/v1/roles/33"
  },
  {
    "rel": "roles",
    "href": "http://localhost:8080/api/v1/roles/34"
  }
]
},

...]

Here are my methods:

@RequestMapping(value = "users", method = RequestMethod.GET, produces = MediaTypes.HAL_JSON_VALUE)
public List<UserResource> list() throws MyException, NotFoundException {
    List<User> userList= userRepository.findAll();
    List<UserResource> resources = new ArrayList<UserResource>();
    for (User user : userList) {
        resources.add(getUserResource(user));
    }
    if(userList == null)
        throw new MyException("List is empty");
    else
        return resources;
}

@RequestMapping(value = "users/{id}", method = RequestMethod.GET)
public UserResource get(@PathVariable Long id) throws NotFoundException {

    User findOne = userRepository.findOne(id);
    if (findOne == null){
        log.error("Unexpected error, User with ID " + id + " not found");
        throw new NotFoundException("User with ID " + id + " not found");
    }
    return getUserResource(findOne);
}

private UserResource getUserResource(User user) throws NotFoundException {
    resource.add(linkTo(UserController.class).slash("users").slash(user.getId()).withSelfRel());
    for(Role role : user.getRoles()){
          resource.add(linkTo(RoleController.class).slash("roles").slash(role.getId()).withRel("roles"));
    }
    return resource;

}

You can see that both methods invoke thegetUserResource(User user) method.

But when i get all users in my database, the format of the _linksis not like i want. I think it must be something about the List of resources i return. Maybe because of that it has no HAL-Format. I also tried a Set instead of List but it gave me the same response


Solution

  • You should return Resources<UserResource> in your list() method.

    return new Resources(resources);
    

    You could also add a self-link to the resources itself to point to the list resource.

    Furthermore, I would suggest using a RessourceAssembler to create the resource instance - see http://docs.spring.io/spring-hateoas/docs/0.23.0.RELEASE/reference/html/#fundamentals.resource-assembler.

    Additionally, you could add paging to your list resource. For this you need to:

    • use the findAll method in your repository that returns a Page<User>.
    • autowire PagedResourcesAssembler<User> in your controller
    • return PagedResources<UserResource> in your list method
    • use the PagedResourcesAssembler to convert the Page into PagedResources

    This would result in something like this:

        private final PagedResourcesAssembler<User> pagedResourcesAssembler;
    
        @RequestMapping(value = "users", method = RequestMethod.GET)
        public ResponseEntity<PagedResources<UserResource>> list(Pageable pageable) {
            final Page<User> page = repository.findAll(pageable);
            final Link link = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(this.getClass()).list(null)).withSelfRel();
    
            PagedResources<UserResource> resources = page.getContent().isEmpty() ?
                    (PagedResources<UserResource>) pagedResourcesAssembler.toEmptyResource(page, ShippingZoneResource.class, link)
                    : pagedResourcesAssembler.toResource(page, resourceAssembler, link);
    
            return ResponseEntity.ok(resources);
        }