我正在为我参与的大型社交网站开发REST API服务.到目前为止,它的工作效果很好.我可以发出GET
,POST
,PUT
和DELETE
请求对象的URL和影响我的数据.但是,此数据被分页(一次限制为30个结果).
但是,通过我的API获取发言人总数的最佳REST方式是什么?
目前,我向URL结构发出请求,如下所示:
/ api / members-返回一个成员列表(如上所述,每次30个成员)
/ api/members/1 -影响单个成员,具体取决于使用的请求方法
我的问题是:如何使用类似的URL结构来获取应用程序中的成员总数?显然id
只需要返回30个结果就可以只请求该字段(类似于Facebook的图谱API)并计算结果是无效的.
虽然对/ API/users的响应被分页并且只返回30条记录,但是没有什么能阻止您在响应中包括记录总数和其他相关信息,如页面大小,页码/偏移量等.
StackOverflow API是同一设计的一个很好的例子.以下是Users方法的文档 - https://api.stackexchange.com/docs/users
我更喜欢使用HTTP标头来获取这种上下文信息.
对于元素总数,我使用X-total-count
标题.
有关下一页,上一页等的链接,我使用http Link
标题:http :
//www.w3.org/wiki/LinkHeader
Github也这样做:https://developer.github.com/v3/#pagination
在我看来它更干净,因为当你返回不支持超链接的内容(即二进制文件,图片)时它也可以使用.
我最近一直在对这个和其他REST分页相关的问题进行一些广泛的研究,并认为在这里添加我的一些发现是有建设性的.我正在扩大这个问题,包括对分页和计数的想法,因为它们是无关紧要的.
寻呼元数据以响应头的形式包含在响应中.这种方法的最大好处是响应有效负载本身就是请求者所要求的实际数据.使对寻呼信息不感兴趣的客户端的响应更容易.
野外使用了大量(标准和自定义)标头来返回与寻呼相关的信息,包括总计数.
X-Total-Count: 234
这在我在野外发现的一些 API中使用.还有NPM包用于添加对此标头的支持,例如Loopback.有些文章也建议设置此标题.
它通常与Link
标题结合使用,这是一种非常好的分页解决方案,但缺少总计数信息.
Link: ; rel="previous"; title*=UTF-8'de'letztes%20Kapitel, ; 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
由一篇名为Range header的博客文章推荐,我选择你(用于分页)!.作者强烈建议使用Range
和Content-Range
标题进行分页.当我们仔细阅读这些标头上的 RFC时,我们发现RFC实际上已经预期将其意义扩展到字节范围之外并且是明确允许的.在items
代替的上下文中使用时bytes
,Range标头实际上为我们提供了一种方法,既可以请求特定范围的项目,也可以指示响应项目与总结果的范围.此标题还提供了显示总计数的好方法.它是一个真正的标准,主要是一对一地映射到分页.它也用于野外.
许多API,包括我们最喜欢的Q&A网站中的 API,都使用信封,这是用于添加有关数据的元信息的数据的包装器.此外,OData和JsonApi标准都使用响应包络.
这个(imho)的一大缺点是处理响应数据变得更加复杂,因为必须在信封的某处找到实际数据.此信封还有许多不同的格式,您必须使用正确的格式.这说明来自OData和JsonApi的响应包络是非常不同的,OData在响应中的多个点处混合了元数据.
我认为在其他答案中已经涵盖了足够的内容.我没有对此进行过多的调查,因为我同意这些令人困惑的评论,因为您现在有多种类型的端点.我认为如果每个端点都代表一个(集合)资源,这是最好的.
我们不仅要传达与响应相关的分页元信息,还要允许客户端请求特定的页面/范围.有趣的是,还要看一下这方面的最终结果是一致的解决方案.在这里我们也可以使用标题(Range
标题似乎非常合适),或其他机制,如查询参数.有些人主张处理结果作为单独的资源,这可能是有意义的一些使用情况下的页面(例如/books/231/pages/52
,我最终选择经常使用的请求参数,如野生范围pagesize
,page[size]
并且limit
等除了支持的Range
标题(和请求参数以及).
您可以将计数作为自定义HTTP标头返回以响应HEAD请求.这样,如果客户端只想要计数,则不需要返回实际列表,也不需要额外的URL.
(或者,如果您处于从端点到端点的受控环境中,则可以使用自定义HTTP谓词,例如COUNT.)
Franci Penov的答案当然是最好的方法,因此您总是会返回项目以及有关您所请求实体的所有其他元数据.这就是它应该做的方式.
但有时返回所有数据没有意义,因为你根本不需要它们.也许您需要的是有关您请求的资源的元数据.像总计数或页数或其他东西.在这种情况下,您始终可以使用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
对于招摇文件:
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实现可以忽略使用其他单位指定的范围。
这表明:使用自定义范围单位不违反协议,但是可以忽略。
这样,我们就必须将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