Spring HATEOAS:仅在分页时显示链接而不序列化 resource/entity
Spring HATEOAS: Only show links when paging without serializing the resource/entity
根据 Spring HATEOAS guide,资源列表的序列化方式是每个资源都显示其内容及其 links:
{
"content": [ {
"price": 499.00,
"description": "Apple tablet device",
"name": "iPad",
"links": [ {
"rel": "self",
"href": "http://localhost:8080/product/1"
} ],
"attributes": {
"connector": "socket"
}
}, {
"price": 49.00,
"description": "Dock for iPhone/iPad",
"name": "Dock",
"links": [ {
"rel": "self",
"href": "http://localhost:8080/product/3"
} ],
"attributes": {
"connector": "plug"
}
} ],
"links": [ {
"rel": "product.search",
"href": "http://localhost:8080/product/search"
} ]
}
在大型数据结构的情况下,我认为最好只向资源提供 links 而不是像这样的资源本身(尤其是在分页时):
{
"_links": {
"items": [{
"href": "http://localhost:8080/product/1"
},{
"href": "http://localhost:8080/product/3"
}]
}
}
除了这会减少传输字节的大小之外,HAL specification 也建议这样做。我目前是这样做的
@RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity root(Pageable pageable, final PagedResourcesAssembler<Entity> assembler) {
Page<Entity> entities = entityRepository.findAll(pageable);
PagedResources<Resource> paged = assembler.toResource(entities,
EntityResourceAssembler.getInstance());
Collection<Resource> resources = paged.getContent();
ResourceSupport support = new ResourceSupport();
for (Resource r : resources) {
Link selfLink = r.getLink(Link.REL_SELF);
support.add(new Link(selfLink.getHref(), "items"));
}
return new ResponseEntity<ResourceSupport>(support, HttpStatus.OK);
}
但这有点难看,因为我 "manually" 需要从资源中获取自身 link。有没有 better/smarter 方法可以实现我想要的?
我将忽略您为什么不想嵌入子资源(因此请求-响应周期更少,客户请求中更昂贵的部分)并尽我所能回答您的问题。
您不能使用内置的 PagedResourceAssembler
来做您想做的事。取而代之的是从 ResourceSupport
派生一些东西,然后自己添加 link。我们在某些情况下这样做,我们只想 link 而不是嵌入资源(因为资源不可用作 HAL)
public class PageResource extends ResourceSupport {
@XmlAttribute(name = "page")
@JsonProperty("page")
private PageMeta pageMeta;
public PageResource() {
this.pageMeta = new PageMeta();
}
public PageMeta getPageMeta() {
return pageMeta;
}
public void setPageMeta(PageMeta pageMeta) {
this.pageMeta = pageMeta;
}
public static class PageMeta {
//Number of resources on this page.
@XmlAttribute @JsonProperty private long size;
//Total number of matching resources
@XmlAttribute @JsonProperty private long totalElements;
//Total number of page
@XmlAttribute @JsonProperty private long totalPages;
//Current page number
@XmlAttribute @JsonProperty private long number;
public PageMeta() {
}
public PageMeta(long size, long totalElements, long totalPages, long number) {
this.size = size;
this.totalElements = totalElements;
this.totalPages = totalPages;
this.number = number;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public long getTotalElements() {
return totalElements;
}
public void setTotalElements(long totalElements) {
this.totalElements = totalElements;
}
public long getTotalPages() {
return totalPages;
}
public void setTotalPages(long totalPages) {
this.totalPages = totalPages;
}
public long getNumber() {
return number;
}
public void setNumber(long number) {
this.number = number;
}
}
}
是一个非常简单的实现,然后在您的控制器中,您负责添加 next 和 prev links,以及项目 links.
此时您可以构建自己的 PagedResourceAssembler
,它只执行 link 而不是嵌入子资源。
除此之外……您应该考虑将 link 添加为 REL "item" 而不是 "items"。很容易想到 link 的集合并为 json 字段键赋予复数名称,但请记住 HAL 规范仅使用字段键作为 link 关系。
以html形式比较
<link rel="items" src="http://1"/>
<link rel="items" src="http://2"/>
到
<link rel="item" src="http://1"/>
<link rel="item" src="http://2"/>
很明显,每个 link 的目标都与集合共享项目关系,而不是 "items",因为每个 link 都针对集合中的单个项目。
根据 Spring HATEOAS guide,资源列表的序列化方式是每个资源都显示其内容及其 links:
{
"content": [ {
"price": 499.00,
"description": "Apple tablet device",
"name": "iPad",
"links": [ {
"rel": "self",
"href": "http://localhost:8080/product/1"
} ],
"attributes": {
"connector": "socket"
}
}, {
"price": 49.00,
"description": "Dock for iPhone/iPad",
"name": "Dock",
"links": [ {
"rel": "self",
"href": "http://localhost:8080/product/3"
} ],
"attributes": {
"connector": "plug"
}
} ],
"links": [ {
"rel": "product.search",
"href": "http://localhost:8080/product/search"
} ]
}
在大型数据结构的情况下,我认为最好只向资源提供 links 而不是像这样的资源本身(尤其是在分页时):
{
"_links": {
"items": [{
"href": "http://localhost:8080/product/1"
},{
"href": "http://localhost:8080/product/3"
}]
}
}
除了这会减少传输字节的大小之外,HAL specification 也建议这样做。我目前是这样做的
@RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity root(Pageable pageable, final PagedResourcesAssembler<Entity> assembler) {
Page<Entity> entities = entityRepository.findAll(pageable);
PagedResources<Resource> paged = assembler.toResource(entities,
EntityResourceAssembler.getInstance());
Collection<Resource> resources = paged.getContent();
ResourceSupport support = new ResourceSupport();
for (Resource r : resources) {
Link selfLink = r.getLink(Link.REL_SELF);
support.add(new Link(selfLink.getHref(), "items"));
}
return new ResponseEntity<ResourceSupport>(support, HttpStatus.OK);
}
但这有点难看,因为我 "manually" 需要从资源中获取自身 link。有没有 better/smarter 方法可以实现我想要的?
我将忽略您为什么不想嵌入子资源(因此请求-响应周期更少,客户请求中更昂贵的部分)并尽我所能回答您的问题。
您不能使用内置的 PagedResourceAssembler
来做您想做的事。取而代之的是从 ResourceSupport
派生一些东西,然后自己添加 link。我们在某些情况下这样做,我们只想 link 而不是嵌入资源(因为资源不可用作 HAL)
public class PageResource extends ResourceSupport {
@XmlAttribute(name = "page")
@JsonProperty("page")
private PageMeta pageMeta;
public PageResource() {
this.pageMeta = new PageMeta();
}
public PageMeta getPageMeta() {
return pageMeta;
}
public void setPageMeta(PageMeta pageMeta) {
this.pageMeta = pageMeta;
}
public static class PageMeta {
//Number of resources on this page.
@XmlAttribute @JsonProperty private long size;
//Total number of matching resources
@XmlAttribute @JsonProperty private long totalElements;
//Total number of page
@XmlAttribute @JsonProperty private long totalPages;
//Current page number
@XmlAttribute @JsonProperty private long number;
public PageMeta() {
}
public PageMeta(long size, long totalElements, long totalPages, long number) {
this.size = size;
this.totalElements = totalElements;
this.totalPages = totalPages;
this.number = number;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public long getTotalElements() {
return totalElements;
}
public void setTotalElements(long totalElements) {
this.totalElements = totalElements;
}
public long getTotalPages() {
return totalPages;
}
public void setTotalPages(long totalPages) {
this.totalPages = totalPages;
}
public long getNumber() {
return number;
}
public void setNumber(long number) {
this.number = number;
}
}
}
是一个非常简单的实现,然后在您的控制器中,您负责添加 next 和 prev links,以及项目 links.
此时您可以构建自己的 PagedResourceAssembler
,它只执行 link 而不是嵌入子资源。
除此之外……您应该考虑将 link 添加为 REL "item" 而不是 "items"。很容易想到 link 的集合并为 json 字段键赋予复数名称,但请记住 HAL 规范仅使用字段键作为 link 关系。
以html形式比较
<link rel="items" src="http://1"/>
<link rel="items" src="http://2"/>
到
<link rel="item" src="http://1"/>
<link rel="item" src="http://2"/>
很明显,每个 link 的目标都与集合共享项目关系,而不是 "items",因为每个 link 都针对集合中的单个项目。