如何 return 来自 @RestController 的 @RepositoryRestResource 样式响应

How to return @RepositoryRestResource style responses from a @RestController

使用@RepositoryRestResource 生成路径并为 REST 注入所有必要的 HATEOAS 链接 API,但是当我 return 使用控制器从存储库中得到相同结果时 JSON结构不同,没有 HATEOAS 链接。

我如何 return 从控制器获得与 RepositoryRestResource 生成的路径相同的 JSON 结构?

// /accounts (RepositoryRestResource JSON structure)
{
    _embedded: {
        accounts: []
    },
    _links: {
        first: {},
        self: {},
        next: {},
        last: {},
        profile: {},
        search: {}
    },
    page: {
    size: 20,
    totalElements: 35,
    totalPages: 2,
    number: 0
    }
}

// /my-accounts (RestController JSON structure)
{
    content: [ ... ], // accounts
    pageable: {},
    totalPages: 1,
    totalElements: 2,
    last: true,
    size: 20,
    number: 0,
    sort: {},
    numberOfElements: 2,
    first: true
}

REST 存储库:

@RepositoryRestResource(collectionResourceRel = "accounts", path = "accounts", itemResourceRel = "account")
public interface AccountRepository extends PagingAndSortingRepository<Account, Long> {

    @RestResource(path = "owner", rel = "owner")
    Page<Account> findByOwner(@Param("username") String owner,Pageable p);
}

REST 控制器:

@RestController
public class AccountController {

    private AccountRepository repo;

    @Autowired
    public AccountController(AccountRepository repo) {
        this.repo = repo;
    }

    @RequestMapping(
        path = "/my-accounts",
        method = RequestMethod.GET,
        produces = "application/hal+json")
    public ResponseEntity<Page<Account>> getStatus(
        @RequestParam(value = "page", defaultValue = "0", required = false) int page,
        @RequestParam(value = "size", defaultValue = "20", required = false) int size,
        Authentication authentication) {

        String username =  authentication.getName();

        Page<Account> accounts = repo.findByOwner(username, PageRequest.of(page, size));

        return ResponseEntity.ok(accounts);
    }
}

不确定,但这可能会成功:

return ResponseEntity.ok(new org.springframework.hateoas.Resource<>(accounts));

如果不是,那么您可以将帐户包装在扩展 ResourceSupport 的 class 中。所以只需创建一些 class AccountSupport extends ResourceSupport 并在其中添加所需的链接。它有很多实用方法,比如

add(linkTo(AccountController.class).withSelfRel());

或个人账户链接:

add(linkTo(AccountController.class).slash(idOfYourAccountInstance).withSelfRel())

基本上,Spring Data REST 只是样板代码(如控制器)的默认实现,人们通常编写这些代码通过 REST 公开 Spring 数据存储库并使用 Spring HATEOAS,即试图用手写控制器重现完全相同的效果意味着只需要自己编写整个 Spring Data REST,因此,这是一个坏主意。幸运的是,某些部分 很容易重现。

如果您只谈论将分页 link 添加到控制器的输出(并且 实现搜索控制器等其他功能,link存在于您的示例 Spring 数据 REST 控制器输出中),您可以查看 how Spring Data REST does it. It uses Spring Data's PagedResourcesAssembler,它接受 Page 并创建具有所需导航 link 的 HATEOAS 资源s.

因此,要添加分页 link,您必须在控制器中注入一个 PagedResourcesAssembler 实例并使用它:

public ResponseEntity<PagedResources> getStatus(
    @RequestParam(value = "page", defaultValue = "0", required = false) int page,
    @RequestParam(value = "size", defaultValue = "20", required = false) int size,
    Authentication authentication,
    PagedResourcesAssembler assembler) {

    String username =  authentication.getName();

    Page<Account> accounts = repo.findByOwner(username, PageRequest.of(page, size));

    return ResponseEntity.ok(assembler.toResource(accounts));
}