我正在为我参与的大型社交网站开发REST API服务。到目前为止,它的运作良好。我可以发行 GET, POST, PUTDELETE 请求反对URL并影响我的数据。但是,该数据进行分页(一次限制为30个结果)。

但是,通过我的API获取成员总数的最佳宁静方法是什么?

目前,我向以下URL结构发出请求:

  • /API/成员- 返回成员列表(上述30个)
  • /API/成员/1- 与单个成员有关,具体取决于使用的要求

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

有帮助吗?

解决方案

虽然对/api/用户的响应进行了分页,并且仅返回30个记录,但没有什么可以阻止您在响应中包括记录总数以及其他相关信息,例如页面大小,页码/偏移等等。 。

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

其他提示

我更喜欢将HTTP标头用于这种上下文信息。

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

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

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

最近,我一直对与此相关的问题进行了一些广泛的研究,并认为在这里添加一些发现是建设性的。我将问题扩展到一些问题,以包括有关分页的想法以及与计数有关的观点。

标题

分页元数据以响应标头的形式包括在响应中。这种方法的最大好处是,响应有效载荷本身只是实际数据请求者所要求的。使处理对分页信息不感兴趣的客户的响应更加容易。

野外使用了一堆(标准和自定义)标题,以返回分页相关的信息,包括总数。

x-total计数

X-Total-Count: 234

这是在 一些 蜜蜂 我在野外发现。也有 NPM软件包 为了将此标头的支持添加到EG loopback。一些 文章 也建议设置此标头。

它通常与 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 标题 使用使用的分页链接使用 rel=next, rel=previous 等等的问题是,它缺乏有关总记录的信息,这就是为什么许多API与此结合的信息 X-Total-Count 标题。

或者,有些API和 jsonapi 标准,使用 Link 格式化,但将信息添加到响应信封中,而不是将信息添加到标题中。这简化了对元数据的访问(并创建一个添加总数信息的地方),以增加访问实际数据本身的复杂性(通过添加信封)为代价。

内容范围

Content-Range: items 0-49/234

由名为的博客文章促进 射程头,我选择您(分页)!. 。作者为使用 RangeContent-Range 分页的标题。当我们仔细阅读时 RFC 在这些标题上,我们发现将其含义扩展到字节范围之外实际上是RFC的预期,并且明确允许。当在上下文中使用 items 代替 bytes, ,范围标头实际上为我们提供了一种要求一定范围的项目,并指出响应项目与之相关的总结果的范围。该标头还提供了一种很好的方式来显示总数。这是一个真正的标准,主要是一对一地映射到分页。也是 在野外使用.

信封

许多API,包括 来自我们最喜欢的问答网站的一个 使用 信封, ,围绕用于添加数据的元信息的数据包装器。还, 奥达塔jsonapi 标准都使用响应信封。

这个(恕我直言)的最大缺点是,由于必须在信封中的某个地方找到实际数据,因此处理响应数据变得更加复杂。此外,该信封也有许多不同的格式,您必须使用正确的格式。它说,来自Odata和Jsonapi的响应包膜大不相同,ODATA在响应中的多个点中混合了元数据。

单独的端点

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

进一步的想法

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

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

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

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

弗朗西·佩诺夫的答案 当然是最好的方法,因此您始终返回物品以及所有有关您的实体的其他元数据。那就是应该这样做的方式。

但是有时返回所有数据没有意义,因为您可能根本不需要它们。也许您需要的只是关于您要求的资源的元数据。例如总数或页数或其他内容。在这种情况下,您总是可以让URL查询告诉您的服务不要返回项目,而只是元数据,例如:

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

或类似的东西...

我建议添加标题相同,例如:

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

对于Swagger文件:

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

从“ X - ” - 前缀弃用。 (看: https://tools.ietf.org/html/rfc6648)

我们发现“接受范围”是绘制分页范围的最佳选择: https://tools.ietf.org/html/rfc7233#section-2.3因为“范围单元”可以是“字节”或“令牌”。两者都不代表自定义数据类型。 (看: https://tools.ietf.org/html/rfc7233#section-4.2)仍然说

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

指示:使用自定义范围单元不违反协议,但可能会忽略。

这样,我们将不得不将接受范围设置为“成员”或任何范围的单位类型。此外,还将内容范围设置为当前范围。 (看: https://www.w3.org/protocols/rfc2616/rfc2616-sec3.html#sec3.12)

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

如果所有前提条件都是正确的,则服务器支持范围
目标资源的标题字段,指定范围为
有效且令人满意(如第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

那新的终点>/api/成员/count刚刚调用成员并返回结果呢?

似乎最容易添加

GET
/api/members/count

并返回成员的总数

有时,框架(例如$ resource/angularjs)需要一个数组作为查询结果,而您实际上不能像 {count:10,items:[...]} 在这种情况下,我将“计数”存储在ResponseHeaders中。

PS实际上,您可以使用$ Resource/angularjs来做到这一点,但需要进行一些调整。

当请求分页数据时,您知道(通过明确的页面大小参数值或默认页面大小值)页面大小,因此您知道是否在响应中获得了所有数据。当响应中的数据少于页面大小时,您就会获取整个数据。返回完整页面后,您必须再次要求另一页。

我更喜欢有单独的端点(或带有参数countonly的相同端点)。因为您可以通过显示正确启动的Progressbar来准备长/耗时的过程。

如果您想在每个响应中返回数据量,则应该有pageSize,也应提及。老实说,最好的方法也是重复请求过滤器。但是反应变得非常复杂。因此,我更喜欢专用端点,而是返回计数。

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

我的库阿奇,更喜欢countonly参数而不是现有端点。因此,当指定时,响应仅包含元数据。

端点?filter =值

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

端点?filter = value&countonly = true

<data>
  <count/>
  <!-- empty list -->
  <list/>
</data>
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top