【问题标题】:How to write paginated controller that expose resource or list of resource in spring-data-hatoas如何编写在 spring-data-hateoas 中公开资源或资源列表的分页控制器
【发布时间】:2026-02-09 15:20:05
【问题描述】:

使用spring-data,我想为我的Person实体写两个方法。

Person.java:

public class Person {
    @Id
    String id;
    String name;
    Integer age;
    // getters/setters omitted for clarity
}

我也写过PersonResouce

public class PersonResource extends Resource<Person> {

    public PersonResource(Person content, Link... links) {
        super(content, links);
    }

}

我还加了一个PersonResourceAssembler

public class PersonResourceAssembler extends ResourceAssemblerSupport<Person, PersonResource> {

    public PersonResourceAssembler() {
        super(PersonController.class, PersonResource.class);
    }

    public PersonResource createResource(Person person) {
        PersonResource personResource = new PersonResource(person);
        Link link = linkTo(PersonController.class).slash(person.getId()).withSelfRel();
        personResource.add(link);
        return personResource;
    }

    @Override
    public PersonResource toResource(Person person) {
        PersonResource resource = createResource(person);
        return resource;
    }
}

这是我的PersonController

@RestController
@RequestMapping("persons")
public class PersonController {

  @Autowired
  private PersonService personService;

  @GetMapping
  public HttpEntity<List<PersonResource>> showAll(@PageableDefault(size = 20) Pageable pageable, PersonDTO condition) {
    Page<Person> page = personService.findAll(pageable, condition);
    Iterable<Person> personList = page.getContent();
    PersonResourceAssembler assembler = new PersonResourceAssembler();
    List<PersonResource> resources = assembler.toResources(personList);
    return new HttpEntity<>(resources);
  }

  @RequestMapping(name = "{id}", produces= MediaType.APPLICATION_JSON_VALUE)
  public HttpEntity<PersonResource> showOne(@PathVariable("id") Long id, PersonDTO condition) {
    condition.setId(id);
    Person person = personService.get(id);
    PersonResourceAssembler assembler = new PersonResourceAssembler();
    PersonResource resource  = assembler.toResource(person);
    return new HttpEntity<>(resource);
  }

}

这是列表的响应:

[
  {
    "createdById": 1,
    "createdDate": "2017-09-21T10:21:05.741Z",
    "deleted": false,
    "email": null,
    "firstName": "User49",
    "id": 52,
    "lastModifiedById": null,
    "lastName": "robot",
    "links": [
      {
        "href": "http://localhost:8080/users/52",
        "rel": "self"
      }
    ],
    "middleName": null,
    "mobile": "010101010001149",
    "roleList": [
      {
        "createdById": null,
        "createdDate": "2017-09-21T10:21:05.580Z",
        "deleted": false,
        "id": 2,
        "lastModifiedById": null,
        "name": "USER",
        "userList": null,
        "version": 0
      }
    ],
    "username": "user49",
    "version": 0
  }
]

这是对一个资源的响应:

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/users/52"
    }
  },
  "createdById": 1,
  "createdDate": "2017-09-21T10:21:05.741Z",
  "deleted": false,
  "email": null,
  "firstName": "User49",
  "id": 52,
  "lastModifiedById": null,
  "lastName": "robot",
  "middleName": null,
  "mobile": "010101010001149",
  "roleList": [
    {
      "createdById": null,
      "createdDate": "2017-09-21T10:21:05.580Z",
      "deleted": false,
      "lastModifiedById": null,
      "name": "USER",
      "userList": null
    }
  ],
  "username": "user49"
}

我查看了documentation,似乎可以使用 PagedResources 来创建分页。

我还希望我的回复看起来像 RepositoryRestController 回复,这意味着列表回复:

  • entities 放在键“_embedded”下
  • links 放在“_links”键下
  • page放在“页面”键下

我尝试过使用PagedResources,但它的工作方式似乎与Resource 不同,并且不能直接替换它。

我希望看到一个使用 PagedResource 的控制器/汇编器。

解决方案

我的解决方案是这样做

  @GetMapping
  public ResponseEntity<?> findAll(PagedResourcesAssembler<Person> pageAssembler, @PageableDefault(size = 20) Pageable pageable, UserDTO condition) {
    Page<User> userList = userService.findAll(pageable, condition);
    PagedResources<?> resources = pageAssembler.toResource(userList, new UserResourceAssembler());
    return ResponseEntity.ok(resources);
  }

【问题讨论】:

    标签: spring spring-mvc spring-data spring-data-rest spring-hateoas


    【解决方案1】:

    尝试使用PagedResourcesAssembler构建分页资源:

    @RestController
    @RequestMapping("persons")
    public class PersonController {
    
        @Autowired private PersonService personService;
        @Autowired private PagedResourcesAssembler<Person> assembler;
        @Autowired private EntityLinks links;
    
        @GetMapping("/paged")
        public ResponseEntity<?> getPaged(Pageable pageable) {
            Page<Person> personsPage = personService.getPaged(pageable);
    
            Link pageSelfLink = links.linkFor(Person.class).slash("/paged").withSelfRel();
            PagedResources<?> resources = assembler.toResource(personPage, this::toResource, pageSelfLink);
    
            return ResponseEntity.ok(resources);
        }
    
        private ResourceSupport toResource(Person person) {
            Link pesonLink = links.linkForSingleResource(person).withRel("person");
            Link selfLink = links.linkForSingleResource(person).withSelfRel();
            return new Resource<>(person, personLink, selfLink);
        }
    }
    

    见我的example

    【讨论】:

    • 我创建了一个具有toResource 方法的UserResourceAssembler。在文档中写到我们应该做create a dedicated class responsible for doing so。在您的示例中困扰我的是您不使用它并创建自己的方法。我想遵循 spring 的建议并使用不同的类,我无法重现您根据我的要求编写的 PageResource 行。
    • 在我的示例中,我使用了 PagedResourcesAssemblertoResource 方法,该方法将自定义汇编器用于页面集合的任何元素,因此我的 toResource 方法是该汇编器的一部分。跨度>
    • 有没有办法将这个toResource 方法放在一个单独的类中?像我制作的 UserResourceAssembler 一样吗?
    • 我已尝试使用您的代码,但显然它不起作用:see screenshot
    • 只需将“UserResourceAssembler resourceAssembler”参数传递给您的控制器方法。 Spring 会为你注入它,然后你可以使用它的toResource() 方法并将它传递给PagedResourcesAssembler$toResource()。您也可以使用PersistentEntityResourceAssembler