返回对象中项目总数的最佳RESTful方法是什么?

问题描述 投票:114回答:11

我正在为我参与的大型社交网站开发REST API服务。到目前为止,它的工作效果很好。我可以向对象URL发出GETPOSTPUTDELETE请求并影响我的数据。但是,此数据被分页(一次限制为30个结果)。

但是,通过我的API获取发言人总数的最佳REST方式是什么?

目前,我向URL结构发出请求,如下所示:

  • / api / members-返回成员列表(如上所述,每次30个)
  • / api / members / 1 - 影响单个成员,具体取决于使用的请求方法

我的问题是:如何使用类似的URL结构来获取应用程序中的成员总数?显然只请求id字段(类似于Facebook的图谱API)并计算结果将是无效的,因为只返回30个结果。

rest restful-url
11个回答
78
投票

虽然对/ API / users的响应被分页并且只返回30条记录,但是没有什么能阻止您在响应中包括记录总数和其他相关信息,如页面大小,页码/偏移量等。

StackOverflow API是同一设计的一个很好的例子。这是Users方法的文档 - https://api.stackexchange.com/docs/users


2
投票

有时候框架(比如$ resource / AngularJS)需要一个数组作为查询结果,在这种情况下你不能像{count:10,items:[...]}这样的响应我在responseHeaders中存储了“count”。

P. S.实际上你可以用$ resource / AngularJS做到这一点,但它需要一些调整。


-1
投票

在请求分页数据时,您知道(通过显式页面大小参数值或默认页面大小值)页面大小,因此您知道是否获得了所有数据作为响应。当响应的数据少于页面大小时,则获得整个数据。返回完整页面时,您必须再次询问另一页。

我更喜欢具有单独的计数端点(或具有参数countOnly的相同端点)。因为您可以通过显示正确启动的进度条来为最终用户准备长时间/耗时的过程。

如果要在每个响应中返回datasize,则应该有pageSize,也提到偏移量。说实话,最好的方法是重复请求过滤器。但是响应变得非常复杂。所以,我更喜欢专用端点返回计数。

<data>
  <originalRequest>
    <filter/>
    <filter/>
  </originalReqeust>
  <totalRecordCount/>
  <pageSize/>
  <offset/>
  <list>
     <item/>
     <item/>
  </list>
</data>

我的Couleage,更喜欢countOnly参数到现有端点。因此,在指定时,响应仅包含元数据。

端点?过滤器=值

<data>
  <count/>
  <list>
    <item/>
    ...
  </list>
</data>

端点?过滤器=值countOnly =真

<data>
  <count/>
  <!-- empty list -->
  <list/>
</data>

61
投票

我更喜欢使用HTTP标头来获取这种上下文信息。

对于元素总数,我使用X-total-count标头。 对于下一页,上一页等的链接,我使用http Link标题: http://www.w3.org/wiki/LinkHeader

Github也这样做:https://developer.github.com/v3/#pagination

在我看来它更干净,因为当你返回不支持超链接的内容(即二进制文件,图片)时也可以使用它。


55
投票

我最近一直在对这个和其他REST分页相关问题进行一些广泛的研究,并认为在这里添加我的一些发现是有建设性的。我正在扩展这个问题,包括对分页和计数的想法,因为它们是无关紧要的。

寻呼元数据以响应头的形式包含在响应中。这种方法的最大好处是响应有效负载本身就是请求者所要求的实际数据。使对寻呼信息不感兴趣的客户端的响应更容易。

野外使用了大量(标准和自定义)标头来返回与寻呼相关的信息,包括总计数。

X-Total-Count

X-Total-Count: 234

这用于我在野外发现的some APIs。还有NPM packages用于添加对此标头的支持,例如环回。一些articles建议也设置这个标题。

它通常与Link标头结合使用,这是一个非常好的寻呼解决方案,但缺少总计数信息。

Link

Link: </TheBook/chapter2>;
      rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
      </TheBook/chapter4>;
      rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel

通过阅读这个主题,我觉得普遍的共识是使用Link header为客户提供使用rel=nextrel=previous等的分页链接。问题在于它缺少有多少总记录的信息,这就是为什么许多API将它与X-Total-Count标头结合起来的原因。

或者,一些API和例如JsonApi标准,使用Link格式,但将信息添加到响应信封而不是标题中。这简化了对元数据的访问(并创建了添加总计数信息的位置),代价是增加了访问实际数据本身的复杂性(通过添加包络)。

Content-Range

Content-Range: items 0-49/234

由一篇名为Range header, I choose you (for pagination)!的博客文章推荐。作者强有力地使用RangeContent-Range标题进行分页。当我们仔细阅读这些标题上的the RFC时,我们发现扩展它们的意义超出字节范围实际上是由RFC预期的并且是明确允许的。当在items而不是bytes的上下文中使用时,Range标头实际上为我们提供了一种方法,既可以请求特定范围的项目,也可以指示响应项目与总结果的范围。此标题还提供了显示总计数的好方法。它是一个真正的标准,主要是一对一地映射到分页。它也是used in the wild

信封

许多API(包括the one from our favorite Q&A website)都使用信封,这是用于添加有关数据的元信息的数据的包装器。此外,ODataJsonApi标准都使用响应包络。

这个(imho)的一大缺点是处理响应数据变得更加复杂,因为必须在信封的某处找到实际数据。此信封还有许多不同的格式,您必须使用正确的格式。这说明来自OData和JsonApi的响应包络是非常不同的,OData在响应中的多个点处混合了元数据。

单独的端点

我认为在其他答案中已经涵盖了足够的内容。我没有对此进行过多的调查,因为我同意这些令人困惑的评论,因为您现在有多种类型的端点。我认为如果每个端点都代表一个(集合)资源,这是最好的。

进一步的想法

我们不仅要传达与响应相关的分页元信息,还要允许客户端请求特定的页面/范围。有趣的是,还要看一下这方面的最终结果是一致的解决方案。在这里我们也可以使用标头(Range标头似乎非常合适),或其他机制,如查询参数。有些人主张将结果页面作为单独的资源处理,这在某些用例中可能有意义(例如/books/231/pages/52。除了支持pagesize标题之外,我最终选择了一系列常用的请求参数,如page[size]limitRange等。 (以及作为请求参数)。


22
投票

您可以将计数作为自定义HTTP标头返回以响应HEAD请求。这样,如果客户端只想要计数,则不需要返回实际列表,也不需要额外的URL。

(或者,如果您处于从端点到端点的受控环境中,则可以使用自定义HTTP谓词,例如COUNT。)


21
投票

当您不需要实际物品时的替代方案

Franci Penov's answer肯定是最好的方式,因此您总是返回项目以及有关您请求的实体的所有其他元数据。这就是它应该做的方式。

但有时返回所有数据没有意义,因为你根本不需要它们。也许您需要的是有关您请求的资源的元数据。像总计数或页数或其他东西。在这种情况下,您始终可以使用URL查询告诉您的服务不要返回项目,而只是返回元数据,如:

/api/members?metaonly=true
/api/members?includeitems=0

或类似的东西......


11
投票

我建议为它添加标题,例如:

HTTP/1.1 200

Pagination-Count: 100
Pagination-Page: 5
Pagination-Limit: 20
Content-Type: application/json

[
  {
    "id": 10,
    "name": "shirt",
    "color": "red",
    "price": "$23"
  },
  {
    "id": 11,
    "name": "shirt",
    "color": "blue",
    "price": "$25"
  }
]

有关详情,请参阅:

https://github.com/adnan-kamili/rest-api-response-format

对于招摇文件:

https://github.com/adnan-kamili/swagger-response-template


5
投票

从“X - ”开始 - 不推荐使用前缀。 (见:https://tools.ietf.org/html/rfc6648

我们发现“Accept-Ranges”是映射分页范围的最佳选择:https://tools.ietf.org/html/rfc7233#section-2.3因为“Range Units”可能是“bytes”或“token”。两者都不代表自定义数据类型。 (见:https://tools.ietf.org/html/rfc7233#section-4.2)尽管如此,据说

HTTP / 1.1实现可以忽略使用其他单元指定的范围。

这表明:使用自定义范围单位不符合协议,但可以忽略它。

这样,我们必须将Accept-Ranges设置为“成员”或任何远程单位类型,我们期望。此外,还将Content-Range设置为当前范围。 (见:https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12

无论哪种方式,我都会坚持RFC7233(https://tools.ietf.org/html/rfc7233#page-8)的建议发送206而不是200:

如果所有前提条件都为真,则服务器支持Range 目标资源的标头字段,以及指定的范围 有效且可满足(如第2.1节中所定义),服务器应该 使用包含一个的有效负载发送206(部分内容)响应 或者对应于可满足的部分表示 要求的范围,如第4节所定义。

因此,我们将拥有以下HTTP标头字段:

对于部分内容:

206 Partial Content
Accept-Ranges: members
Content-Range: members 0-20/100

完整内容:

200 OK
Accept-Ranges: members
Content-Range: members 0-20/20

2
投票

那么刚刚调用Members.Count()并返回结果的新端点> / api / members / count怎么样?


2
投票

似乎最容易添加一个

GET
/api/members/count

并返回会员总数

© www.soinside.com 2019 - 2024. All rights reserved.