我正在为我参与的大型社交网站开发REST API服务。到目前为止,它的工作效果很好。我可以向对象网址发出GET
,POST
,PUT
和DELETE
个请求并影响我的数据。但是,此数据已被分页(一次限制为30个结果)。
但是,通过我的API获取会员总数的最佳REST方式是什么?
目前,我向URL结构发出请求,如下所示:
我的问题是:我如何使用类似的网址结构来获取应用程序中的成员总数?显然只请求id
字段(类似于Facebook的图谱API)并计算结果将是无效的,因为只返回30个结果。
答案 0 :(得分:76)
虽然对/ API / users的响应进行了分页并且只返回30条记录,但是没有什么能阻止您在响应中包含记录总数和其他相关信息,例如页面大小,页码/偏移等。
StackOverflow API是同一设计的一个很好的例子。以下是Users方法的文档 - https://api.stackexchange.com/docs/users
答案 1 :(得分:59)
我更喜欢使用HTTP标头来获取这种上下文信息。
对于元素总数,我使用X-total-count
标题
对于下一页,上一页等的链接,我使用http Link
标题:
http://www.w3.org/wiki/LinkHeader
Github也这样做:https://developer.github.com/v3/#pagination
在我看来它更干净,因为当你返回不支持超链接的内容(即二进制文件,图片)时也可以使用它。
答案 2 :(得分:49)
我最近一直在对这个和其他REST分页相关问题进行一些广泛的研究,并认为在这里添加我的一些发现是有建设性的。我稍微扩展了一下这个问题,包括对分页和计数的看法,因为它们是无关紧要的。
分页元数据以响应头的形式包含在响应中。这种方法的最大好处是响应有效负载本身就是请求者要求的实际数据。使对寻呼信息不感兴趣的客户端的响应更容易。
野外使用了大量(标准和自定义)标头来返回与寻呼相关的信息,包括总计数。
X-Total-Count: 234
这是我在野外发现的some APIs中使用的。还有NPM packages用于添加对此标头的支持,例如环回。有些articles建议也设置此标头。
它通常与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=next
,rel=previous
等向客户提供分页链接。这就是它缺少有多少总记录的信息,这就是许多API将其与X-Total-Count
标题相结合的原因。
或者,一些API和例如JsonApi标准,使用Link
格式,但将信息添加到响应信封中而不是标头中。这简化了对元数据的访问(并创建了添加总计数信息的位置),但代价是增加了访问实际数据本身的复杂性(通过添加信封)。
Content-Range: items 0-49/234
由名为Range header, I choose you (for pagination)!的博客文章推广。作者强烈建议使用Range
和Content-Range
标题进行分页。当我们仔细阅读这些标题上的the RFC时,我们发现RFC实际上已经预期将其意义扩展到字节范围之外,并且是明确允许的。在items
而不是bytes
的上下文中使用时,Range标头实际上为我们提供了一种方法,既可以请求特定范围的项目,也可以指示响应项目与总结果的范围。此标题还提供了显示总计数的好方法。它是一个真正的标准,主要是一对一地映射到分页。它也是used in the wild。
许多API(包括the one from our favorite Q&A website)使用信封,这是用于添加有关数据的元信息的数据的包装器。此外,OData和JsonApi标准都使用响应信封。
这个(imho)的一大缺点是处理响应数据变得更加复杂,因为必须在信封的某处找到实际数据。此信封还有许多不同的格式,您必须使用正确的格式。它告诉OData和JsonApi的响应包络是完全不同的,OData在响应中的多个点混合元数据。
我认为在其他答案中已经涵盖了足够的内容。我没有对此进行过多的调查,因为我同意这些令人困惑的评论,因为您现在有多种类型的端点。如果每个端点代表一个(资源集合),我认为这是最好的。
我们不仅需要传达与响应相关的分页元信息,还允许客户端请求特定的页面/范围。有趣的是,还要看一下这方面的最终结果是一致的解决方案。在这里我们也可以使用标头(Range
标头似乎非常合适),或其他机制,如查询参数。有些人主张将结果页面视为单独的资源,这在某些用例中可能有意义(例如/books/231/pages/52
。我最终选择了一系列常用的请求参数,例如pagesize
,{{1除了支持page[size]
标头(以及请求参数)之外,还有limit
等。
答案 3 :(得分:22)
您可以将计数作为自定义HTTP标头返回以响应HEAD请求。这样,如果客户端只想要计数,则不需要返回实际列表,也不需要额外的URL。
(或者,如果您处于从端点到端点的受控环境中,则可以使用自定义HTTP谓词,例如COUNT。)
答案 4 :(得分:21)
Franci Penov's answer肯定是最好的方式,因此您始终会返回项目以及有关您请求的实体的所有其他元数据。这就是它应该做的方式。
但有时返回所有数据没有意义,因为您可能根本不需要它们。也许您需要的是有关您请求的资源的元数据。像总计数或页数或其他东西。在这种情况下,您始终可以使用URL查询告诉您的服务不要返回项目,而只是返回元数据,如:
/api/members?metaonly=true
/api/members?includeitems=0
或类似的......
答案 5 :(得分: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
对于招摇文件:
答案 6 :(得分:3)
从“ 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实现可以忽略使用其他指定的范围 单位。
这表明:使用自定义范围单位不违反协议,但是可以忽略。
这样,我们必须将Accept-Ranges设置为“ members”或我们期望的任何范围单位类型。此外,还将“内容范围”设置为当前范围。 (请参阅: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
答案 7 :(得分:2)
新终点怎么样&gt; / api / members / count只调用Members.Count()并返回结果
答案 8 :(得分:2)
似乎最容易添加
GET
/api/members/count
并返回会员总数
答案 9 :(得分:2)
有时框架(比如$ resource / AngularJS)需要一个数组作为查询结果,在这种情况下你不能真正得到像{count:10,items:[...]}
这样的响应我存储&#34; count&#34;作为回应.Headers。
P上。 S.实际上你可以用$ resource / AngularJS做到这一点,但它需要一些调整。
答案 10 :(得分:0)
有关设计REST API以返回多个对象计数的有趣讨论: https://groups.google.com/g/api-craft/c/qbI2QRrpFew/m/h30DYnrqEwAJ?pli=1
作为API使用者,我希望每个计数值都可以表示 作为可计算资源的子资源(即GET / tasks / count表示任务数),或者作为更大字段 与相关资源(即GET)相关的元数据的汇总 / tasks / metadata)。通过对同一父项下的相关端点进行范围界定 资源(即/ tasks),API变得直观,并且 通常可以从端点的路径和HTTP方法推断出端点。
其他想法:
- 如果每个单独的计数仅与其他计数结合使用(例如,对于统计信息显示板),则可以 公开一个端点,该端点聚集并返回所有计数 一次。
- 如果您已有一个用于列出所有资源的端点(即GET / tasks用于列出所有任务),则该计数可以包含在 响应作为元数据(作为HTTP标头或在响应正文中)。 这样做会给API造成不必要的负载,这可能是 可以忽略不计,具体取决于您的用例。
答案 11 :(得分:-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>
答案 12 :(得分:-1)
您可以考虑将counts
作为资源。该网址将为:
/api/counts/member