如何正确使用PagedResourcesAssembler从春季的数据?如何正确使用PagedRes

2019-06-17 10:03发布

我使用Spring 4.0.0.RELEASE,春数据共享1.7.0.M1,春天HATEOAS 0.8.0.RELEASE

我的资源是一个简单的POJO:

public class UserResource extends ResourceSupport { ... }

我的资源汇编用户对象转换为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);
}

Answer 1:

你似乎已经发现了使用正确的方式,但我想进入这里的一些细节有点为他人找为好。 我走进类似的详细介绍PagedResourceAssembler在这个答案 。

表示模型

春天HATEOAS附带的表示模型,这样更容易创造配备链接表示各种基类。 有三种类型的提供开箱即用的类:

  • Resource -项目资源。 有效地环绕一些DTO或实体捕获单个项目,并与链接丰富它。
  • Resources -集合资源,即可以出头的集合,但通常是集合Resource的情况。
  • PagedResources -的扩展Resources ,抓住像总页数等的数量额外分页信息

所有这些类派生自ResourceSupport ,这是一个基本的容器Link的情况。

资源汇编

一个ResourceAssembler现在是减轻组件添加到您的域对象或DTO的转换为这样的资源实例。 这里最重要的部分是,它变成一个源对象到一个目标对象。

所以PagedResourcesAssembler将采取一个Spring数据Page实例,并将其改造成一个PagedResources通过对实例Page ,并建立必要的PageMetadata以及在prevnext链接来浏览网页。 默认情况下-这大概是这里最有趣的部分-这将使用普通的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);

外表

作为即将到来的弹簧数据共享1.7 RC1(和Spring HATEOAS 0.9传递地)所述的prevnext链接将作为生成RFC6540兼容URI模板以暴露配置分页请求参数HandlerMethodArgumentResolversPageableSort

您在上面所示的配置可以通过注释与配置类简化@EnableSpringDataWebSupport这将让你摆脱掉所有的bean的声明。



Answer 2:

我想资源列表转换为页面。 但给人当它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);
    }


Answer 3:

另一种方式

另一种方式是使用Range HTTP标头(在读更多RFC 7233 )。 您可以定义HTTP头是这样的:

Range: resources=20-41

这意味着,你想从20至41(含)获得资源。 这种方式可以让API的consuments收到确切定义的资源。

这只是另一种方式。 范围通常与另一个单元中使用(如字节等)

推荐的方式

如果您想分页的工作和有真正适用的API(超媒体/ HATEOAS包含),那么我建议添加页面和每页到您的网址。 例:

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; }
}


文章来源: How to correctly use PagedResourcesAssembler from Spring Data?