来自RESTful API的分页响应有效内容

我想在我的RESTful API中支持分页。

我的API方法应该通过/products/index返回产品的JSON列表。 但是,可能有成千上万的产品,我想通过它们进行翻页,所以我的请求应该是这样的:

 /products/index?page_number=5&page_size=20 

但是,我的JSON响应需要什么样子? API消费者通常期望在响应中使用分页元数据吗? 或者只是一系列必要的产品? 为什么?

它看起来像Twitter的API包含元数据: https : //dev.twitter.com/docs/api/1/get/lists/members (请参阅示例请求)。

元数据:

 { "page_number": 5, "page_size": 20, "total_record_count": 521, "records": [ { "id": 1, "name": "Widget #1" }, { "id": 2, "name": "Widget #2" }, { "id": 3, "name": "Widget #3" } ] } 

只是一个产品arrays(没有元数据):

 [ { "id": 1, "name": "Widget #1" }, { "id": 2, "name": "Widget #2" }, { "id": 3, "name": "Widget #3" } ] 

ReSTful API主要被其他系统使用,这就是为什么我把分页数据放在响应头里的原因。 但是,某些API使用者可能无法直接访问响应头,或者可能正在通过API构buildUX,因此提供一种方法(按需)检索JSON响应中的元数据是一个好的方法。

我相信你的实现应该包含机器可读的元数据作为默认值,并且在请求时包含可读的元数据。 如果您喜欢,或者最好是通过查询参数(例如include=metadatainclude_metadata=true进行按需,则可读的元数据可以随每个请求一起返回。

在您的特定情况下,我会包含每个产品的logging的URI。 这使API消费者可以轻松创build到单个产品的链接。 我也会根据我的寻呼请求的限制设置一些合理的期望。 实现和logging页面大小的默认设置是可以接受的做法。 例如, GitHub的API将默认页面大小设置为30个logging(最大值为100),并对可以查询API的次数设置速率限制。 如果您的API具有默认页面大小,则查询string可以只指定页面索引。

在人类可读的情况下,当导航到/products?page=5&per_page=20&include=metadata ,响应可能是:

 { "_metadata": { "page": 5, "per_page": 20, "page_count": 20, "total_count": 521, "Links": [ {"self": "/products?page=5&per_page=20"}, {"first": "/products?page=0&per_page=20"}, {"previous": "/products?page=4&per_page=20"}, {"next": "/products?page=6&per_page=20"}, {"last": "/products?page=26&per_page=20"}, ] }, "records": [ { "id": 1, "name": "Widget #1", "uri": "/products/1" }, { "id": 2, "name": "Widget #2", "uri": "/products/2" }, { "id": 3, "name": "Widget #3", "uri": "/products/3" } ] } 

对于机器可读的元数据,我会添加链接头到响应:

 Link: </products?page=5&perPage=20>;rel=self,</products?page=0&perPage=20>;rel=first,</products?page=4&perPage=20>;rel=previous,</products?page=6&perPage=20>;rel=next,</products?page=26&perPage=20>;rel=last 

(链接标题值应该被urlencoded)

…可能还有一个自定义total-count响应头,如果你这样select的话:

 total-count: 521 

在以人为中心的元数据中显示的其他分页数据对于以机器为中心的元数据可能是多余的,因为链接标题让我知道我在哪页以及每页的数量,并且我可以快速地检索数组中的logging数。 因此,我可能只会为总数创build一个标题。 您可以随时改变主意并添加更多的元数据。

另外,您可能会注意到我从您的URI中删除/index 普遍接受的约定是让你的ReST端点公开集合。 /index在最后泥泞略有上升。

这些只是消费/创buildAPI时我喜欢的一些东西。 希望有所帮助!

作为编写REST服务的几个库的人,请让我把客户的观点告诉您为什么我认为将结果包装到元数据中是一种可行的方法:

  • 没有总数,客户怎么知道它还没有收到所有的东西,应该继续在结果集中分页? 在没有执行下一页的UI中,在最坏的情况下,这可能表示为Next / More链接,它实际上并没有获取更多的数据。
  • 在响应中包括元数据允许客户端跟踪更less的状态。 现在,我不必将REST请求与响应进行匹配,因为响应包含重build请求状态所需的元数据(在本例中为光标到数据集中)。
  • 如果状态是响应的一部分,我可以同时对同一个数据集执行多个请求,并且我可以按照它们发生的任何顺序处理这些请求,而这些请求不一定是我提出请求的顺序。

和一个build议:像Twitter的API ,你应该用直的索引/光标replacepage_number。 原因是,API允许客户端设置每个请求的页面大小。 返回的page_number是到目前为止客户端请求的页面数量,还是给出了上次使用的page_size的页面数量(几乎肯定是后面的,但为什么不完全避免这种模糊性)?

我会build议添加相同的标头。 将元数据移动到标题可以帮助摆脱resultdatarecords等信封,而响应主体只包含我们需要的数据。 如果您也生成分页链接,则可以使用链接标题。

  HTTP/1.1 200 X-Pagination-Count: 100 X-Pagination-Page: 5 X-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