有没有在Spring中实现HATEOAS +分页的简单方法,而不使用data rest?

huangapple go评论65阅读模式
英文:

Is there a simple implementation of HATEOAS + Pagination in Spring without data rest?

问题

以下是您提供的内容的翻译部分:

{
    "_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"
                    }
                }
            }
        ]
    }
}

请注意,这是您提供的内容的翻译版本,仅包含原始内容的翻译,没有附加的回答或解释。如果您有任何其他翻译需求,请随时提问。

英文:

I've been searching for a few days on how to implement a Spring REST API containing HATEOAS links + Pagination with Spring boot and JPA (No spring data rest) like this random example:

{
"_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"
}
}
}
]
}
}

However, the solutions that I've found so far are ridiculously complicated or the amount of boilerplate are laughable
This solution for instance:
https://howtodoinjava.com/spring5/hateoas/pagination-links/
the tutorial on the page doesn't present it entirely but it requires you to create the entity, a model for the entity, and a long RepresentationModelAssemblerSupport full of boilerplate

I've tried this also:
https://spring.io/guides/tutorials/rest/
However the nested classes (i have relations of one to many/many to one) don't get the links for 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"
}
}
}

I mean, isn't there a simpler solution? Luckily for pagination PagingAndSortingRepository is useful but then having both pagination + HATEOAS, what a nightmare.

答案1

得分: 10

`RepresentationModelAssembler`是必需的如果不需要额外的字段那么一个模型类`extends EntityModel<T>`是不必要的

我的示例实体类是`Inventory`。

RepresentationModelAssembler你可以在这里添加更多链接
```java
@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上为它点赞。

英文:

A RepresentationModelAssembler is required. A model class extends EntityModel<T> is unnecessary if you don't need additional fields.

My example entity class is Inventory.

RepresentationModelAssembler. You can add more links here.

@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());
    }
}

Controller

@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 response. A full long response can be found in my HTML doc. Download it and open with your browser.

{
  "_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
  }
}

Full code is shared in Github. If my project is helpful other than this answer, consider give it a star in Github.

答案2

得分: 0

这个例子中有一个很好的解释,附带有代码。
Pagination(with Hateoas), Filtering & Sorting with Spring Boot and JPA
演示了多种方法。这正好涵盖了在没有使用Spring Data Rest的情况下实现分页、排序和过滤的方法。

英文:

There is an excellent explanation here in this example with code
Pagination(with Hateoas), Filtering & Sorting with Spring Boot and JPA
Multiple approaches are demonstrated. This covers exactly the appraoch to implement pagination, including sorting and filtering without spring data rest.

huangapple
  • 本文由 发表于 2020年9月19日 06:32:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/63963571.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定