J'utilise Spring 4.0.0.RELEASE, Spring Data Commons 1.7.0.M1, Spring Hateoas 0.8.0.RELEASE
Ma ressource est un simple POJO:
public class UserResource extends ResourceSupport { ... }
Mon assembleur de ressources convertit les objets User en objets UserResource:
@Component
public class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> {
public UserResourceAssembler() {
super(UserController.class, UserResource.class);
}
@Override
public UserResource toResource(User entity) {
// map User to UserResource
}
}
Dans mon UserController, je veux récupérer Page<User>
De mon service, puis le convertir en PagedResources<UserResource>
En utilisant PagedResourcesAssembler
, comme affiché ici: https://stackoverflow.com/a/16794740/1321564
@RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(@PageableDefault Pageable p, PagedResourcesAssembler assembler) {
Page<User> u = service.get(p)
return assembler.toResource(u);
}
Cela n'appelle pas UserResourceAssembler
et simplement le contenu de User
est retourné à la place de mon UserResource
personnalisé.
Le retour d'une seule ressource fonctionne:
@Autowired
UserResourceAssembler assembler;
@RequestMapping(value="{id}", method=RequestMethod.GET)
UserResource getById(@PathVariable ObjectId id) throws NotFoundException {
return assembler.toResource(service.getById(id));
}
PagedResourcesAssembler
veut un argument générique, mais je ne peux pas utiliser T toResource(T)
, car je ne veux pas convertir mon Page<User>
En PagedResources<User>
, surtout parce que User
est un POJO et aucune ressource.
La question est donc: comment ça marche?
Mon WebMvcConfigurationSupport:
@Configuration
@ComponentScan
@EnableHypermediaSupport
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(pageableResolver());
argumentResolvers.add(sortResolver());
argumentResolvers.add(pagedResourcesAssemblerArgumentResolver());
}
@Bean
public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
return new HateoasPageableHandlerMethodArgumentResolver(sortResolver());
}
@Bean
public HateoasSortHandlerMethodArgumentResolver sortResolver() {
return new HateoasSortHandlerMethodArgumentResolver();
}
@Bean
public PagedResourcesAssembler<?> pagedResourcesAssembler() {
return new PagedResourcesAssembler<Object>(pageableResolver(), null);
}
@Bean
public PagedResourcesAssemblerArgumentResolver pagedResourcesAssemblerArgumentResolver() {
return new PagedResourcesAssemblerArgumentResolver(pageableResolver(), null);
}
/* ... */
}
@Autowired
UserResourceAssembler assembler;
@RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(@PageableDefault Pageable p, PagedResourcesAssembler pagedAssembler) {
Page<User> u = service.get(p)
return pagedAssembler.toResource(u, assembler);
}
Vous semblez avoir déjà découvert la bonne façon de l'utiliser, mais j'aimerais entrer un peu dans certains détails ici pour que d'autres les trouvent également. Je suis entré dans les détails similaires sur PagedResourceAssembler
dans cette réponse .
Spring HATEOAS est livré avec une variété de classes de base pour les modèles de représentation qui facilitent la création de représentations équipées de liens. Il existe trois types de classes prêtes à l'emploi:
Resource
- une ressource d'élément. Pour envelopper efficacement un DTO ou une entité qui capture un élément unique et l'enrichit de liens.Resources
- une ressource de collection, qui peut être une collection de quelque chose mais qui est généralement une collection d'instances de Resource
.PagedResources
- une extension de Resources
qui capture des informations de pagination supplémentaires comme le nombre total de pages, etc.Toutes ces classes dérivent de ResourceSupport
, qui est un conteneur de base pour les instances de Link
.
Un ResourceAssembler
est désormais le composant atténuant pour convertir vos objets de domaine ou DTO en de telles instances de ressources. La partie importante ici est qu'elle transforme un objet source en un objet cible.
Ainsi, le PagedResourcesAssembler
prendra une instance de Spring Data Page
et la transformera en une instance de PagedResources
en évaluant le Page
et en créant le PageMetadata
nécessaire ainsi que le prev
et next
liens pour naviguer dans les pages. Par défaut - et c'est probablement la partie intéressante ici - il utilisera une simple SimplePagedResourceAssembler
(une classe interne de PRA
) pour transformer les éléments individuels de la page en instances imbriquées Resource
.
Pour permettre de personnaliser cela, PRA
a des méthodes toResource(…)
supplémentaires qui prennent un délégué ResourceAssembler
pour traiter les éléments individuels. Vous vous retrouvez donc avec quelque chose comme ceci:
class UserResource extends ResourceSupport { … }
class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { … }
Et le code client ressemble maintenant à ceci:
PagedResourcesAssembler<User> parAssembler = … // obtain via DI
UserResourceAssembler userResourceAssembler = … // obtain via DI
Page<User> users = userRepository.findAll(new PageRequest(0, 10));
// Tell PAR to use the user assembler for individual items.
PagedResources<UserResource> pagedUserResource = parAssembler.toResource(
users, userResourceAssembler);
À partir du prochain Spring Data Commons 1.7 RC1 (et Spring HATEOAS 0.9 de manière transitoire), les liens prev
et next
seront générés sous la forme de modèles d'URI conformes RFC654 pour exposer les paramètres de demande de pagination configuré dans HandlerMethodArgumentResolvers
pour Pageable
et Sort
.
La configuration que vous avez montrée ci-dessus peut être simplifiée en annotant la classe config avec @EnableSpringDataWebSupport
qui vous permettrait de vous débarrasser de toutes les déclarations explicites du bean.
Je voulais convertir la liste des ressources en page. mais en lui donnant PagedResourcesAssembler, il mangeait les liens internes.
Cela permettra de paginer votre liste.
public class JobExecutionInfoResource extends ResourceSupport {
private final JobExecutionInfo jobExecution;
public JobExecutionInfoResource(final JobExecutionInfo jobExecution) {
this.jobExecution = jobExecution;
add(ControllerLinkBuilder.linkTo(methodOn(JobsMonitorController.class).get(jobExecution.getId())).withSelfRel()); // add your own links.
}
public JobExecutionInfo getJobExecution() {
return jobExecution;
}
}
Ressource paginée Fournir ResourceAssembler indiquant à la ressource paginée de l'utiliser, ce qui ne fait rien simplement la renvoyer car c'est déjà une liste de ressources qui est passée.
private final PagedResourcesAssembler<JobExecutionInfoResource> jobExecutionInfoResourcePagedResourcesAssembler;
public static final PageRequest DEFAULT_PAGE_REQUEST = new PageRequest(0, 20);
public static final ResourceAssembler<JobExecutionInfoResource, JobExecutionInfoResource> SIMPLE_ASSEMBLER = entity -> entity;
@GetMapping("/{clientCode}/{propertyCode}/summary")
public PagedResources<JobExecutionInfoResource> getJobsSummary(@PathVariable String clientCode, @PathVariable String propertyCode,
@RequestParam(required = false) String exitStatus,
@RequestParam(required = false) String jobName,
Pageable pageRequest) {
List<JobExecutionInfoResource> listOfResources = // your code to generate the list of resource;
int totalCount = 10// some code to get total count;
Link selfLink = linkTo(methodOn(JobsMonitorController.class).getJobsSummary(clientCode, propertyCode, exitStatus, jobName, DEFAULT_PAGE_REQUEST)).withSelfRel();
Page<JobExecutionInfoResource> page = new PageImpl<>(jobExecutions, pageRequest, totalCount);
return jobExecutionInfoResourcePagedResourcesAssembler.toResource(page, SIMPLE_ASSEMBLER, selfLink);
}