Description
We have a REST service which is implement with SpringBoot and HATEOAS. The following method exists within the WebService
@Override
public ResponseEntity<?> getCar(String carId) {
Car car = carService.findById(carId).orElseThrow(() -> {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Car not found!");
});
// toDTO & HATEOAS Links
CarItem carItem = carModelAssembler.toModel(car);
HalModelBuilder builder = HalModelBuilder.halModelOf(carItem);
builder.embed(garageModelAssembler.toModel(car.getGarage()), LinkRelation.of("garage"));
return ResponseEntity.ok(builder.build());
}
The response looks like this (abbreviated):
{
"id":"xyu",
"garageId":"096a07ea-3970-4968-9cd9-4756ae6a9cb1",
"_embedded": {
"garage":{
"id":"abc",
"location":"XY",
"_links":{
"self":{
"href":"https://localhost:8443/api/v1/garages/096a07ea-3970-4968-9cd9-4756ae6a9cb1"
},
}
}
},
"_links": {
"self": {
"href":"https://localhost:8443/api/v1/cars/d589852f-0b3f-4025-86cb-2fa3b35a1339"
}
}
}
We would also like to output the embedded objects in our paged responses. We came up with the following as a solution:
@Component
public class CarModelAssembler implements RepresentationModelAssembler<Car, RepresentationModel<CarItem>> {
private GarageModelAssembler garageModelAssembler;
private CarMapper carMapper;
public BuildingModelAssembler(CarMapper carMapper, GarageModelAssembler garageModelAssembler) {
this.carMapper = carMapper;
this.garageModelAssembler = garageModelAssembler;
}
@Override
public RepresentationModel<CarItem> toModel(Car car) {
return toModel(car, true);
}
public RepresentationModel<CarItem> toModel(Car car, boolean embedded) {
CarItem carItem = locationMapper.CarToCarItem(car);
HalModelBuilder builder = HalModelBuilder.halModelOf(carItem);
// add Links
builder.link(linkTo(methodOn(CarController.class).getBuilding(carItem.getId())).withSelfRel());
builder.link(linkTo(methodOn(GarageController.class).getGarage(carItem.getGarage.getId())).withRel("garage"));
if(embedded) {
builder.embed(garageModelAssembler.toModel(car.getGarage(), false));
}
return builder.<CarItem>build();
}
}
The call in the controller is then as follows:
CollectionModel carItems = pagedResourcesAssembler.toModel(pCars, carModelAssembler);
The whole thing works in principle, but I don't like the fact that:
a. - I have to pack another RepresentationModel (RepresentationModel) around my CarItem (extends RepresentationModel).
b. - I still have to install the embedded switch in the assembler so that there are no infinite nestings
c. - I have to inject the required assemblers for the embedded objects everywhere, which means that sooner or later I have to work with @lazy
Unfortunately, I cannot use the HalRepresentationModel directly because it is private
I'm hoping someone here has a better idea.