I have a question concerning the representation model processors of Spring HATEOAS. We are experimenting to process models before serializing them to the client. Our use case is to enrich the imageUrl
field of UserModel
objects at runtime, as we have to build the URL based on values from a config bean (AWS S3 bucket URL differs for DEV / PROD setup).
@Data
public class UserModel {
// ...
private String imageUrl;
}
Therefore, we create a UserProcessor
to implement this:
public class UserProcessor implements RepresentationModelProcessor<EntityModel<UserModel>> {
private final ConfigAccessor configAccessor;
public UserProcessor(ConfigAccessor configAccessor) {
this.configAccessor = configAccessor;
}
@Override
public EntityModel<UserModel> process(EntityModel<UserModel> model) {
if (model.getContent() != null)
// do the enrichment and set "imageUrl" field
}
return model;
}
}
This works perfectly if we have a controller method like this:
@ResponseBody
@GetMapping("/me")
public EntityModel<UserModel> getCurrentUser(@AuthenticationPrincipal Principal principal) {
UserModel user = ... // get user model
return EntityModel.of(user);
}
However, we are struggling now with the enrichment whenever a UserModel
is referenced in another model class, e.g., the BookModel
:
@Data
public class BookModel {
private String isbn;
// ...
private EntityModel<UserModel> user; // or "private UserModel user;"
}
A controller method returning type EntityModel<BookModel>
only applies the processor for its type, but not for types that are referenced. It seems the processors are not applied recursively.
Is this intentional or are we doing something wrong?
Thanks for any input and help, Michael
By default, Spring hateoas generated responses are in application/hal+json format. It is the default mediatype even if we pass application/json as well. In HAL, the _links entry is a JSON object. The property names are link relations and each value is single or multiple links. 2. Spring HATEOAS RepresentationModel Example
Spring HATEOAS. Spring HATEOAS provides some APIs to ease creating REST representations that follow the HATEOAS principle when working with Spring and especially Spring MVC. The core problem it tries to address is link creation and representation assembly.
In this article, we're going to build an example using Spring HATEOAS with the goal of decoupling the client and server, and theoretically allowing the API to change its URI scheme without breaking clients. 3. Preparation First, let's add the Spring HATEOAS dependency:
The methodOn () obtains the method mapping by making dummy invocation of the target method on the proxy controller and sets the customerId as the path variable of the URI. 7. Spring HATEOAS in Action Let's put the self-link and method link creation all together in a getAllCustomers () method:
I encountered the same issue and I resolved it by manually assembling resources, in your case that would be implementing RepresentationModelAssembler of the BookModel and then manually invoking the processor on the userModel object that is inside the book.
First consider the BookModel to extend RepresentationModel so that you can manually add links and assemble inner resources (which you would like for the EntityModel<UserModel> object)
@Data
public class BookModel extends RepresentationModel<BookModel> {...}
Now write the assembler that takes your book entity and transforms it into a representation model or a collection of these models. You will implement here what EntityModel.of(...) does for you automagically.
@Component
public class BookModelAssembler implements RepresentationModelAssembler<Book, BookModel> {
@Autowired
private UserProcessor userProcessor;
@Override
public BookModel toModel(Book entity) {
var bookModel = new BookModel(entity) // map fields from entity to model
// Transform the user entity to an entity model of user
var user = entity.getUser();
EntityModel<UserModel> userModel = EntityModel.of(user);
userModel = userProcessor.process(userModel);
bookModel.setUserModel(userModel);
return bookModel;
}
}
I might be going out on a limb but I suppose the reason for this is that the processors get invoked when an MVC endpoint returns a type that has a registered processor, which in the case of embedded types is not invoked. My reasoning is based on the docs for RepresentationModelProcessor, which states that processor processes representation models returned from Spring MVC controllers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With