Can I make a custom controller mirror the formatting of Spring-Data-Rest / Spring-Hateoas generated classes?
Solution 1:
I've found a way to imitate the behavior of Spring Data Rest completely. The trick lies in using a combination of the PagedResourcesAssembler
and an argument-injected instance of PersistentEntityResourceAssembler
. Simply define your controller as follows...
@RepositoryRestController
@RequestMapping("...")
public class ThingController {
@Autowired
private PagedResourcesAssembler pagedResourcesAssembler;
@SuppressWarnings("unchecked") // optional - ignores warning on return statement below...
@RequestMapping(value = "...", method = RequestMethod.GET)
@ResponseBody
public PagedResources<PersistentEntityResource> customMethod(
...,
Pageable pageable,
// this gets automatically injected by Spring...
PersistentEntityResourceAssembler resourceAssembler) {
Page<MyEntity> page = ...;
...
return pagedResourcesAssembler.toResource(page, resourceAssembler);
}
}
This works thanks to the existence of PersistentEntityResourceAssemblerArgumentResolver
, which Spring uses to inject the PersistentEntityResourceAssembler
for you. The result is exactly what you'd expect from one of your repository query methods!
Solution 2:
Updated answer on this old question: You can now do that with a PersistentEntityResourceAssembler
Inside your @RepositoryRestController:
@RequestMapping(value = "somePath", method = POST)
public @ResponseBody PersistentEntityResource postEntity(@RequestBody Resource<EntityModel> newEntityResource, PersistentEntityResourceAssembler resourceAssembler)
{
EntityModel newEntity = newEntityResource.getContent();
// ... do something additional with new Entity if you want here ...
EntityModel savedEntity = entityRepo.save(newEntity);
return resourceAssembler.toResource(savedEntity); // this will create the complete HATEOAS response
}
Solution 3:
I believe I've solved this problem in a fairly straightforward way, although it could have been better documented.
After reading the implementation of SimplePagedResourceAssembler
I realized a hybrid solution might work. The provided Resource<?>
class renders entities correctly, but doesn't include links, so all you need to do is add them.
My QuestionResourceAssembler
implementation looks like this:
@Component
public class QuestionResourceAssembler implements ResourceAssembler<Question, Resource<Question>> {
@Autowired EntityLinks entityLinks;
@Override
public Resource<Question> toResource(Question question) {
Resource<Question> resource = new Resource<Question>(question);
final LinkBuilder lb =
entityLinks.linkForSingleResource(Question.class, question.getId());
resource.add(lb.withSelfRel());
resource.add(lb.slash("answers").withRel("answers"));
// other links
return resource;
}
}
Once that's done, in my controller I used Option 2 above:
return pagedResourcesAssembler.toResource(page, questionResourceAssembler);
This works well, and isn't too much code. The only hassle is you need to manually add links for each reference you need.