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 都针对集合中的单个项目。