我喜欢一些一些帮助处理一个奇怪的边缘情况与分页API我正在建设。
与许多api一样,这个api也会分页较大的结果。如果你查询/foos,你会得到100个结果(即foo #1-100),和一个链接到/foos?Page =2,返回foo #101-200。
不幸的是,如果在API使用者进行下一次查询之前从数据集中删除了foo #10, /foos?Page =2将偏移100并返回foos #102-201。
这对于试图获取所有foo的API使用者来说是一个问题——他们不会收到foo #101。
处理这种情况的最佳实践是什么?我们希望使它尽可能的轻量级(即避免为API请求处理会话)。来自其他api的示例将非常感谢!
分页通常是一个“用户”操作,为了防止计算机和人脑的过载,通常会给出一个子集。然而,与其认为我们没有得到完整的列表,不如问问它重要吗?
如果需要一个精确的实时滚动视图,本质上是请求/响应的REST api并不适合这个目的。为此,你应该考虑WebSockets或HTML5 Server-Sent Events,让你的前端知道何时处理更改。
现在,如果需要获得数据的快照,我将只提供一个API调用,在一个请求中提供所有数据,而不进行分页。请注意,如果您有一个大型数据集,您将需要一些可以执行输出流而不临时将其加载到内存中的东西。
对于我的例子,我隐式地指定了一些API调用来允许获取全部信息(主要是引用表数据)。您还可以保护这些api,使其不会损害您的系统。
参考API分页设计,我们可以通过游标来设计分页API
他们有一个概念,叫做游标,它是指向一行的指针。你可以对数据库说"在那之后返回100行"对于数据库来说,这要容易得多,因为很有可能通过带索引的字段来标识行。这样你就不需要获取和跳过这些行了,你可以直接跳过它们。
一个例子:
GET /api/products
{"items": [...100 products],
"cursor": "qWe"}
API返回一个(不透明的)字符串,你可以使用它来检索下一页:
GET /api/products?cursor=qWe
{"items": [...100 products],
"cursor": "qWr"}
实现方面有许多选项。通常,您有一些排序标准,例如,产品id。在这种情况下,您将使用一些可逆算法(比如哈希)对产品id进行编码。在接收到带有游标的请求时,对其进行解码并生成类似WHERE id >:cursor LIMIT 100的查询。
优势:
通过游标可以提高数据库的查询性能
处理好时,新内容插入到db查询
劣势:
使用无状态API生成前一个页面链接是不可能的
我不完全确定您的数据是如何处理的,因此这可能有效,也可能无效,但是您是否考虑过使用时间戳字段进行分页?
当你查询/foos时,你会得到100个结果。你的API应该返回如下内容(假设是JSON,但如果它需要XML,也可以遵循相同的原则):
{
"data" : [
{ data item 1 with all relevant fields },
{ data item 2 },
...
{ data item 100 }
],
"paging": {
"previous": "http://api.example.com/foo?since=TIMESTAMP1"
"next": "http://api.example.com/foo?since=TIMESTAMP2"
}
}
只是一个注释,只使用一个时间戳依赖于结果中的隐式“限制”。您可能希望添加显式限制,或者也使用until属性。
时间戳可以使用列表中的最后一个数据项动态确定。这似乎或多或少是Facebook在其Graph API中的分页方式(向下滚动到底部,以我上面给出的格式查看分页链接)。
一个问题可能是,如果您添加了一个数据项,但根据您的描述,听起来它们将被添加到最后(如果没有,请告诉我,我将看看是否可以改进这一点)。
分页通常是一个“用户”操作,为了防止计算机和人脑的过载,通常会给出一个子集。然而,与其认为我们没有得到完整的列表,不如问问它重要吗?
如果需要一个精确的实时滚动视图,本质上是请求/响应的REST api并不适合这个目的。为此,你应该考虑WebSockets或HTML5 Server-Sent Events,让你的前端知道何时处理更改。
现在,如果需要获得数据的快照,我将只提供一个API调用,在一个请求中提供所有数据,而不进行分页。请注意,如果您有一个大型数据集,您将需要一些可以执行输出流而不临时将其加载到内存中的东西。
对于我的例子,我隐式地指定了一些API调用来允许获取全部信息(主要是引用表数据)。您还可以保护这些api,使其不会损害您的系统。
RESTFul api中的另一个分页选项是使用这里介绍的Link头。例如,Github使用它如下:
Link: <https://api.github.com/user/repos?page=3&per_page=100>; rel="next",
<https://api.github.com/user/repos?page=50&per_page=100>; rel="last"
rel的可能值是:first, last, next, previous。但是通过使用Link头,可能无法指定total_count(元素的总数)。