Spring HATEOAS:仅在分页时显示链接而不序列化资源/实体

时间:2015-04-29 12:29:30

标签: spring rest spring-mvc hateoas spring-hateoas

根据Spring HATEOAS guide,资源列表的序列化方式是每个资源都显示其内容及其链接:

{
    "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": {
      "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);
}

但这有点难看,因为我&#34;手动&#34;需要从资源中获取自我链接。是否有更好/更聪明的方法来实现我想要的目标?

1 个答案:

答案 0 :(得分:2)

我将忽略为什么你不想嵌入子资源(因此请求响应周期较少,客户端请求中更昂贵的部分)并尽可能地回答你的问题。

您无法使用内置PagedResourceAssembler来执行您想要的操作。而是从ResourceSupport派生一些东西,然后自己添加链接。我们在某些情况下执行此操作,我们只想链接而不是嵌入资源(因为资源不可用作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链接以及项链接。

此时,您可以构建自己的PagedResourceAssembler,它只会执行链接,而不是嵌入子资源。

小一点......你应该考虑将链接添加为REL“item”而不是“items”。考虑链接集合并给json字段键赋予复数名称是很诱人的,但请记住HAL规范只使用字段键作为链接关系。

以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"/>

很明显,每个链接的目标与集合共享一个项目关系,而不是“项目”,因为每个链接都是集合中的一个项目。