我正在使用Spring 4.0.0.RELEASE,Spring Data Commons 1.7.0.M1,Spring Hateoas 0.8.0.RELEASE
我的资源是一个简单的POJO:
public class UserResource extends ResourceSupport { ... }
我的资源汇编程序将User对象转换为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
}
}
在我的UserController中,我想从我的服务中检索Page<User>
,然后使用PagedResources<UserResource>
将其转换为PagedResourcesAssembler
,如下所示: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);
}
这不会调用UserResourceAssembler
,只会返回User
的内容,而不是我的自定义UserResource
。
返回单个资源:
@Autowired
UserResourceAssembler assembler;
@RequestMapping(value="{id}", method=RequestMethod.GET)
UserResource getById(@PathVariable ObjectId id) throws NotFoundException {
return assembler.toResource(service.getById(id));
}
PagedResourcesAssembler
需要一些通用参数,但后来我无法使用T toResource(T)
,因为我不想将Page<User>
转换为PagedResources<User>
,尤其是因为User
是POJO,没有资源。
所以问题是:它是如何工作的?
我的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);
}
答案 0 :(得分:66)
您似乎已经发现了正确的使用方法,但我想在此处详细介绍其他一些细节以供其他人查找。我在this answer中详细介绍了PagedResourceAssembler
。
Spring HATEOAS附带了各种表示模型的基类,可以轻松创建配备链接的表示。开箱即用的类有三种类型:
Resource
- 项目资源。有效地包装一些DTO或实体,用于捕获单个项目并通过链接丰富它。Resources
- 一个集合资源,可以是某些东西的集合,但通常是Resource
个实例的集合。PagedResources
- Resources
的扩展,可以捕获额外的分页信息,例如总页数等。所有这些类都派生自ResourceSupport
,它是Link
个实例的基本容器。
ResourceAssembler
现在是将域对象或DTO转换为此类资源实例的缓解组件。这里的重要部分是,它将一个源对象转换为一个目标对象。
因此,PagedResourcesAssembler
将采用Spring Data Page
实例,并通过评估PagedResources
并将必要的Page
创建为PageMetadata
来将其转换为prev
实例以及用于浏览网页的next
和SimplePagedResourceAssembler
链接。默认情况下 - 这可能是这里有趣的部分 - 它将使用普通的PRA
(内部类Resource
)将页面的各个元素转换为嵌套的PRA
实例。
要允许自定义此选项,toResource(…)
还有其他ResourceAssembler
方法,可让代理 class UserResource extends ResourceSupport { … }
class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { … }
处理各个项目。所以你最终得到这样的东西:
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);
客户端代码现在看起来像这样:
prev
截至即将发布的Spring Data Commons 1.7 RC1(以及Spring HATEOAS 0.9),next
和HandlerMethodArgumentResolvers
链接将生成为符合RFC6540的URI模板,以显示分页请求参数在Pageable
Sort
和@EnableSpringDataWebSupport
配置。
您可以通过使用{{1}}注释配置类来简化您在上面显示的配置,这将使您摆脱所有显式bean声明。
答案 1 :(得分:0)
我想将资源列表转换为页面。但是当给它PagedResourcesAssembler时,它吞噬了内部链接。
这将使您的列表分页。
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;
}
}
分页资源提供ResourceAssembler告诉分页资源使用它,它什么也不做简单地返回它,因为它已经是传递的资源列表。
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);
}
答案 2 :(得分:-6)
替代方式
另一种方法是使用Range HTTP标头(在RFC 7233中阅读更多内容)。您可以通过以下方式定义HTTP标头:
Range: resources=20-41
这意味着,您希望从20到41(包括)获取资源。这种方式允许API的使用接收精确定义的资源。
这只是替代方式。范围通常与其他单位(如字节等)一起使用
推荐方式
如果您想使用分页并且具有真正适用的API(包括超媒体/ HATEOAS),那么我建议您将URL和PageSize添加到您的URL。例如:
http://host.loc/articles?Page=1&PageSize=20
然后,您可以在BaseApiController中读取此数据并在所有请求中创建一些QueryFilter对象:
{
var requestHelper = new RequestHelper(Request);
int page = requestHelper.GetValueFromQueryString<int>("page");
int pageSize = requestHelper.GetValueFromQueryString<int>("pagesize");
var filter = new QueryFilter
{
Page = page != 0 ? page : DefaultPageNumber,
PageSize = pageSize != 0 ? pageSize : DefaultPageSize
};
return filter;
}
你的api应该返回一些特殊的集合,其中包含有关项目数量的信息。
public class ApiCollection<T>
{
public ApiCollection()
{
Data = new List<T>();
}
public ApiCollection(int? totalItems, int? totalPages)
{
Data = new List<T>();
TotalItems = totalItems;
TotalPages = totalPages;
}
public IEnumerable<T> Data { get; set; }
public int? TotalItems { get; set; }
public int? TotalPages { get; set; }
}
您的模型类可以继承一些具有分页支持的类:
public abstract class ApiEntity
{
public List<ApiLink> Links { get; set; }
}
public class ApiLink
{
public ApiLink(string rel, string href)
{
Rel = rel;
Href = href;
}
public string Href { get; set; }
public string Rel { get; set; }
}