几天来我一直在寻找如何实现包含 HATEOAS 链接的 Spring REST API + 使用 Spring boot 和 JPA 进行分页(无 spring 数据休息),就像这个随机示例:
{
"_links": {
"first": {
"href": "http://localhost:8080/api/albums-list?page=0&size=2&sort=title,desc"
},
"prev": {
"href": "http://localhost:8080/api/albums-list?page=0&size=2&sort=title,desc"
},
"self": {
"href": "http://localhost:8080/api/albums-list?page=1&size=2&sort=title,desc"
},
"next": {
"href": "http://localhost:8080/api/albums-list?page=2&size=2&sort=title,desc"
},
"last": {
"href": "http://localhost:8080/api/albums-list?page=4&size=2&sort=title,desc"
}
},
"page": {
"size": 2,
"totalElements": 10,
"totalPages": 5,
"number": 1
},
"_embedded": {
"albums": [
{
"id": 7,
"title": "Top Hits Vol 7",
"description": "Top hits vol 7. description",
"releaseDate": "10-03-1987",
"actors": [
{
"id": 4,
"firstName": "Janice",
"lastName": "Preston",
"_links": {
"self": {
"href": "http://localhost:8080/api/actors/4"
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:8080/api/actors/7"
}
}
},
{
"id": 6,
"title": "Top Hits Vol 6",
"description": "Top hits vol 6. description",
"releaseDate": "10-03-1986",
"actors": [
{
"id": 3,
"firstName": "Laverne",
"lastName": "Mann",
"_links": {
"self": {
"href": "http://localhost:8080/api/actors/3"
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:8080/api/actors/6"
}
}
}
]
}
}
然而,到目前为止我找到的解决方案非常复杂,或者样板文件的数量非常可笑 这个解决方案例如: https://howtodoinjava.com/spring5/hateoas/pagination-links/ 页面上的教程并没有完全展示它,但它需要您创建实体、实体模型和一个充满样板的长 RepresentationModelAssemblerSupport
我也试过这个: https://spring.io/guides/tutorials/rest/ 但是嵌套类(我有一对多/多对一的关系)没有获得 HATEOAS 的链接:
{
"id": 3,
"nome": "Amazonas",
"uf": "AM",
"cidades": [
{
//no HATEOAS in here
"id": 10003,
"nome": null,
"instituicoes": [],
"uf": "AM"
},
{
"id": 219,
"nome": "Alvarães",
"instituicoes": [],
"uf": "AM"
}
],
"_links": {
"self": {
"href": "http://localhost:8080/api/v1/estados/estadoes/3"
},
"estadoes": {
"href": "http://localhost:8080/api/v1/estados/estadoes"
}
}
}
我的意思是,难道没有更简单的解决方案吗?幸运的是,分页 PagingAndSortingRepository 很有用,但同时具有分页 + HATEOAS,真是一场噩梦。
A
RepresentationModelAssembler
是必需的。如果您不需要其他字段,则不需要模型类extends EntityModel<T>
。
我的示例实体类是
Inventory
.
RepresentationModelAssembler。您可以在此处添加更多链接。
@Component
public class InventoryModelAssembler implements RepresentationModelAssembler<Inventory, EntityModel<Inventory>> {
@Override
public EntityModel<Inventory> toModel(Inventory inventory) {
return EntityModel.of(inventory,
linkTo(methodOn(InventoryController.class).get(inventory.getId()))
.withSelfRel());
}
}
控制器
@RestController
@RequestMapping("/inventories")
@RequiredArgsConstructor
public class InventoryController {
private final InventoryRepository inventoryRepository;
private final PagedResourcesAssembler pagedResourcesAssembler;
private final InventoryModelAssembler inventoryModelAssembler;
@GetMapping
public ResponseEntity get(Pageable pageable) {
Page<Inventory> inventories = inventoryRepository.findAll(pageable);
return ResponseEntity
.ok()
.contentType(MediaTypes.HAL_JSON)
.body(pagedResourcesAssembler.toModel(inventories, inventoryModelAssembler));
}
}
HAL JSON 响应。可以在我的 HTML 文档 中找到完整的长回复。下载并使用浏览器打开。
{
"_embedded" : {
"inventories" : [ {
"carrier" : "SG",
"fltNum" : "001",
"serviceType" : "PAX",
"fltDate" : "2020-01-20",
"fltDow" : 1,
"createdDate" : "2020-06-11T13:21:44.992Z",
"lastModifiedDate" : "2020-06-11T13:21:44.992Z",
"_links" : {
"self" : {
"href" : "http://localhost:8080/inventories/5ee22fe8853b0f45ae5fca27"
}
}
}]
},
"_links" : {
"first" : {
"href" : "http://localhost:8080/inventories?carrier=SG&fltNum=001&page=0&size=10&sort=fltNum,asc&sort=fltDate,desc"
},
"self" : {
"href" : "http://localhost:8080/inventories?carrier=SG&fltNum=001&page=0&size=10&sort=fltNum,asc&sort=fltDate,desc"
},
"next" : {
"href" : "http://localhost:8080/inventories?carrier=SG&fltNum=001&page=1&size=10&sort=fltNum,asc&sort=fltDate,desc"
},
"last" : {
"href" : "http://localhost:8080/inventories?carrier=SG&fltNum=001&page=1&size=10&sort=fltNum,asc&sort=fltDate,desc"
}
},
"page" : {
"size" : 10,
"totalElements" : 20,
"totalPages" : 2,
"number" : 0
}
}
完整代码在Github中共享。如果我的项目对这个答案有帮助,请考虑在 Github 中给它一个星号。
在这个例子中用代码有一个很好的解释 分页(使用 Hateoas),使用 Spring Boot 和 JPA 进行过滤和排序 展示了多种方法。这完全涵盖了实现分页的方法,包括没有 spring data rest 的排序和过滤。