0
votes

I've been checking similar issues and haven't found any answer addressing what I am observing.

The issue is that I've easily managed to get Hypermedia in HAL format in my REST API when I retrieve 1 resource, but when I hit the controller methods retrieving a list of entities, then the hypermedia is NOT the same.

Here are the ouputs:

  • single resource returned

    "_links": { "self": { "href": "http://localhost:8080/celsvs/api/books/123567891099" }, "books": { "href": "http://localhost:8080/celsvs/api/books" } }

  • List of resources

    "links": [ { "rel": "self", "href": "http://localhost:8080/celsvs/api/books/123567891099" }, { "rel": "books", "href": "http://localhost:8080/celsvs/api/books" } ]

I started with Spring hateoas 0.25, but as I had to uplift anyway Spring boot and I saw that the Hateoas API had changed, I am now on Spring hateoas 1.0... And even after adapting my code to the new API I am still getting the same result.

I am using the RepresentationModelAssemblerSupport class to keep my controllers clean from code to generate hateoas content. So this is how it looks like:

@Component
public class BookModelAssembler extends RepresentationModelAssemblerSupport<BookDto, BookDto> {

    public BookModelAssembler() {
        super(BooksController.class, BookDto.class);
    }

    @Override
    public BookDto toModel(BookDto entity) {
        return entity.add(linkTo(methodOn(BooksController.class).getResource(entity.getIsbn())).withSelfRel())
                     .add(linkTo(methodOn(BooksController.class).getAllResources()).withRel("books"));
    }
    
}

And in the Controller, the endpoints to retrieve one or all resources:

@Override
@GetMapping(value = {QueryConstants.BOOKS_ISBN_PATH, QueryConstants.BOOKS_SIG_PATH})
public BookDto getResource(@PathVariable("isbn") final String isbn) {
    
    return this.modelAssembler.toModel(this.findOneResource(isbn));
}


// GET - retrieve all resources

@Override
@GetMapping (produces = { "application/hal+json" })
public List<BookDto> getAllResources() {
    
    return (findAllResources().stream().map(this.modelAssembler::toModel).collect(Collectors.toList()));
}

As you can see, the Hypermedia rendered is different even when all the entities in the list returned have been mapped using the same method toModel() used in the method getResource().

The only way I've managed to see in the case of all resources the proper HAL format returned is when I've changed the implementation of the controller to return a collection Model:

//@GetMapping
public CollectionModel<BookDto> getAll() {
        return this.modelAssembler.toCollectionModel(findAllResources());
}

but then all the entities are bundled inside an _embedded element, which is NOT what I want when I return the collection of entities.

Spring Hateoas documentation states that HAL is the default, so I have not thought about configuring anything for now...

So, I only see:

  • I am missing some configuration so that when I can get a collection of entities rendered (no under the _embedded element)... But I haven't seen anything suitable in HalConfiguration bean.
  • I am assuming that the proper way of returning the collection of the type of resources requested is NOT inside the _embedded property... but maybe I am wrong. So far my understanding was that when you request, say, resource Person and you want to return it's contacts, those being also directly reachable via their own endpoint, then you embed those in the content returned along with the rest of Person properties... I haven't found anything stating that collections are expected to be rendered inside the _embedded property.

Does anyone have any advice? I am running out of time and ideas and the team implementing the client side is waiting to consume the Hypermedia content. Thanks!

2

2 Answers

1
votes

The HAL specification says that the property _embedded is used to store an array of resource object.

Edit: To answer Alberto's question in his own answer

Still, if someone can tell me why in the previous implementation the attached links did not follow the HAL format, I would appreciate. Thanks

Spring HATEOAS customizes JSON serialization of RepresentationModel which is the parent class of CollectionModel.

// org.springframework.hateoas.mediatype.hal.RepresentationModelMixin
public abstract class RepresentationModelMixin extends RepresentationModel<RepresentationModelMixin> {

    @Override
    @JsonProperty("_links")
    @JsonInclude(Include.NON_EMPTY)
    @JsonSerialize(using = Jackson2HalModule.HalLinkListSerializer.class)
    @JsonDeserialize(using = Jackson2HalModule.HalLinkListDeserializer.class)
    public abstract Links getLinks();
}

@JsonProperty("_links") defines the JSON property name to be _links. @JsonSerialize defines the serializer to be used. Look into method org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize for serialization logic.

0
votes

After some more reading I've made my mind and I have concluded that the proper thing to send back in the response is a collection inside the _embedded element.

I've followed several references:

  • Section 6 of JSON HAL rfc draft (this is the latest version I've found), there is an example of a document returned as a result of a request of a list of resources, where the list of them is embedded in an array inside the _embedded element.
  • In a section dedicated to Collections by other writers, the interpretation is the same and the elements of a collection are returned inside an _embedded property.

So in line with this, if I change my controller to return CollectionModel, then I get a proper content formatted in HAL.

The code being:

@GetMapping 
public CollectionModel<BookDto> getAll() {
    
    return this.modelAssembler.toCollectionModel(findAllResources()).add(linkTo(methodOn(BooksController.class).getAll()).withSelfRel());
}

And the result being:

{
"_embedded": {
    "bookDtoList": [
        {
            "isbn": "123567891099",
            "signature": "AA-23-EEE",
            "title": "Electromagnetismo",
            "subtitle": "Introducción a las aplicaciones del electromagnetismo",
            "authors": [
                "Idoia Mendieta",
                "Bonifacio Pérez"
            ],
            "available": false,
            "numOfCopies": 0,
            "library": null,
            "detailedInfo": null,
            "_links": {
                "self": {
                    "href": "http://localhost:8080/celsvs/api/books/123567891099"
                },
                "books": {
                    "href": "http://localhost:8080/celsvs/api/books"
                }
            }
        },
        {
            "isbn": "123567891012",
            "signature": "AA-23-EFD",
            "title": "Electromagnetismo",
            "subtitle": "Introducción a las aplicaciones del electromagnetismo",
            "authors": [
                "Idoia Mendieta",
                "Bonifacio Pérez"
            ],
            "available": false,
            "numOfCopies": 0,
            "library": null,
            "detailedInfo": null,
            "_links": {
                "self": {
                    "href": "http://localhost:8080/celsvs/api/books/123567891012"
                },
                "books": {
                    "href": "http://localhost:8080/celsvs/api/books"
                }
            }
        }
    ]
},
"_links": {
    "self": {
        "href": "http://localhost:8080/celsvs/api/books"
    }
}

}

Still, if someone can tell me why in the previous implementation the attached links did not follow the HAL format, I would appreciate. Thanks