RESTful API设计中的分页问题

5
我正在设计一个用于移动应用的RESTful API。我的问题是如何处理包含许多项的大型集合。我知道一种好的做法是对集合中的大量结果进行分页。
我已经阅读了Facebook Graph API文档 (https://developers.facebook.com/docs/graph-api/using-graph-api/v2.2),Twitter cursors文档 (https://dev.twitter.com/overview/api/cursoring),GitHub API文档 (https://developer.github.com/v3/) 和这篇文章 (API pagination best practices)。
考虑一个名为/resources的示例集合在我的API中包含100个按降序排序的项目,从resource1resource100。当您发出GET请求 (GET http://api.path.com/resources?limit=5)时,您将获得以下响应:
{
    "_links": {
        "self": { "href": "/resources?limit=5&page=1" },
        "last": { "href": "/resources?limit=5&page=7" },
        "next": { "href": "/resources?limit=5&page=2" }
    },

    "_embedded": {
        "records": [ 
            { resource 100 },
            { resource 99 },
            { resource 98 },
            { resource 97 },
            { resource 96 }
        ]
    }
}

现在我的问题是这样的情景:
1- 我使用上述内容获取 /resources
2- 之后,资源集合中添加了一些内容(比如说另一个设备为此帐户添加了新资源)。因此,我现在有101个资源。
3- 根据初始响应,我使用 /resources?limit=5&page=2 获取下一页结果。响应如下:
{
    "_links": {
        "self": { "href": "/history?page=2&limit=5" },
        "last": { "href": "/history?page=7&limit=5" },
        "next": { "href": "/history?page=3&limit=5" }
    },

    "_embedded": {
        "records": [ 
            { resource 96 },
            { resource 95 },
            { resource 94 },
            { resource 93 },
            { resource 92 }
        ]
    }
}

正如您所看到的,资源96在两个页面中都重复出现(或者如果在第2步中删除了资源,则可能会发生类似的问题,在这种情况下,一个资源将会丢失)。

由于我想在移动应用程序和一个列表中使用它,因此我必须将每个API调用的资源附加到之前的资源上,以便我可以拥有完整的列表。但是这很麻烦。如果您有建议,请告诉我。提前致谢。

附言:我已经考虑过像查询字符串一样的时间戳而不是基于游标的分页,但那会给我带来其他问题。(如果您需要更多信息,请告诉我。)


为什么不同时使用基于游标的分页和时间戳呢? - Jonathan W
2个回答

3
我们刚刚通过 REST API 为移动应用程序实现了类似的功能。移动应用程序传递了一个额外的查询参数,表示页面中的元素“冻结”的时间戳。因此,你的第一个请求将看起来像 GET /resources?limit=5&page=1&from=2015-01-25T05:10:31.000Z,然后第二个页面请求(一段时间之后)将增加页数,但保持相同的时间戳:GET /resources?limit=5&page=2&from=2015-01-25T05:10:31.000Z。这还使移动应用程序可以控制是否要区分“软”页面(保留页面 1 请求的时间戳)和“硬刷新”页面(将时间戳重置为当前时间)。

@JonathanW 那么你什么时候更新并保存“from”值呢? - Petar
在我参与的移动应用程序中,当发出下拉刷新时,应用程序本身会执行该操作。在向列表底部进行无限滚动时,它将发送相同的时间戳。明白了吗? - Jonathan W
@JonathanW 是的,这很有道理,谢谢。你能总结一下后端如何处理吗?我的意思是,基于时间戳,你如何知道返回什么信息? - Petar
根据项目创建时间戳以降序排列页面,从客户端传递的时间开始向后工作。 - Jonathan W
@Mepla,我现在也在解决同样的问题。起初,我只使用时间戳+限制(由于可变时间戳,偏移量不需要),但是我发现这种方法不合适,因为时间戳的覆盖范围。如果我将限制设置为20,但每毫秒有21条消息(由于服务器端的某些延迟可能是真实的),那么我就会完全丢失第21条消息。在这种情况下,我现在只使用所需实体的主要数据库ID。在我的情况下,这是可以接受的,因为它是不可变的并且ID递增(因此也可以用作时间戳)。 - Nexen
显示剩余3条评论

0
为什么不维护一组已经查看过的资源呢?
然后,当您处理每个响应时,可以检查该资源是否已经被呈现。

这只会防止某些内容在视图中重复出现,但在更低的层面上,我正在浪费时间执行无法获得所需结果的API调用。(想象一下如果添加了二十个资源后我的第二步骤) - Mepla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接