Mastodon

Spring HATEOAS "links" vs "_links" and JSON Formats

Recently, I added Spring HATEOAS to the IT Hub. Refactoring the controller to return RepresentationModel and CollectionModel, I noticed something odd. When returning a collection of items, in the returned JSON, the links for each item were represented under “links”. When returning a single item, the links for this item were represented under “_links”. This article briefly explains the reasons for this and how to fix it.

Researching the issue, I realized two things. First, my Spring configuration was missing something crucial. Spring HATEOAS works, but it has to be configured correctly. I learned that there’s a difference between returning JSON and something called HAL-JSON. Returning a link-collection in plain JSON will result in the links being in “links”, while in HAL-JSON, they are in “_links”. HAL is the “Hypertext Application Language” and is one of many representation of resources. There is also “collection + json”, Hydra and Siren. Spring has to be configured to return all resource-representations in the same format.

The second problem was the returned class in the controller method. The endpoint for multiple items returned a java.util.list of these objects. The controller method for only one item did the right thing: returning a RepresentationModel.

After fixing both issues, this is the working code (the important parts):

@RestController
@RequestMapping("/api/groups")
public class GroupController {
 
    private final GroupRepository groupRepository;
 
    @Autowired
    public GroupController(GroupRepository groupRepository) {
        this.groupRepository = groupRepository;
    }
 
    @GetMapping(value = "")
    public ResponseEntity<CollectionModel<GroupModel>> getAllGroups() {
 
        List<GroupModel> groupModels = groupRepository
                .findAllByOrderByNameAsc()
                .stream()
                .map((group) -> new GroupResourceAssembler(this.getClass(), GroupModel.class).toModel(group))
                .collect(Collectors.toList());
        return ResponseEntity.ok(new CollectionModel<>(groupModels));
    }
}

Here’s the WebConfig to set the returned JSON format to HAL JSON:

@Configuration
@EnableWebMvc
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
public class WebConfig implements WebMvcConfigurer {
 
	// ...
}