REST Web应用程序中的分页

时间:2009-04-22 09:53:59

标签: rest sorting pagination

这是this question的更通用的重构(删除了Rails特定部分)

我不确定如何在RESTful Web应用程序中的资源上实现分页。 假设我有一个名为products的资源,您认为以下哪种方法是最佳方法,以及原因:

1。仅使用查询字符串

例如。 http://application/products?page=2&sort_by=date&sort_how=asc
这里的问题是我不能使用整页缓存,而且URL也不是很干净且容易记住。

2。使用页面作为资源和查询字符串进行排序

例如。 http://application/products/page/2?sort_by=date&sort_how=asc
在这种情况下,看到的问题是http://application/products/pages/1不是唯一的资源,因为使用sort_by=price会产生完全不同的结果我仍然可以'使用页面缓存。

3。使用页面作为资源和URL段进行排序

例如。 http://application/products/by-date/page/2
我个人认为使用这种方法没有问题,但有人警告我这不是一个好方法(他没有给出理由,所以如果你知道为什么它不是推荐,请告诉我)

任何建议,意见和批评都非常受欢迎。感谢。

13 个答案:

答案 0 :(得分:103)

我同意Fionn的意见,但我会更进一步,并告诉我,Page是资源,它是请求的属性。这使我只选择了选项1查询字符串。感觉很对。我真的很喜欢Twitter API的结构是如何安排的。不是太简单,也不是太复杂,记录良好。无论好坏,这都是我的“走向”设计,当我在做某种方式而不是另一种方式的时候。

答案 1 :(得分:62)

我认为版本3的问题更多是“观点”问题 - 您是否将该页面视为资源或页面上的产品。

如果您将该页面视为资源,那么这是一个非常好的解决方案,因为第2页的查询将始终产生第2页。

但是如果您看到页面上的产品作为资源,您就会遇到第2页上的产品可能会发生变化(旧产品被删除或其他)的问题,在这种情况下,URI并不总是 返回相同的资源。

E.g。客户存储指向产品列表页面X的链接,下次打开链接时,相关产品可能不再位于第X页。

答案 2 :(得分:35)

HTTP具有很好的Range标头,也适用于分页。你可以发送

Range: pages=1

只有第一页。这可能会迫使你重新思考什么是页面。也许客户想要一系列不同的商品。范围标题也可用于声明订单:

Range: products-by-date=2009_03_27-

获取比该日期更新的所有产品或

Range: products-by-date=0-2009_11_30

让所有产品都比该日期更早。 '0'可能不是最佳解决方案,但RFC似乎想要一些范围启动。部署的HTTP解析器可能无法解析单位= -range_end。

如果header不是(可接受的)选项,我认为第一个解决方案(查询字符串中的所有内容)是一种处理页面的方法。但请,请按字母顺序规范化查询字符串(sort(key = value)对)。这解决了“?a = 1& b = x”和“?b = x& a = 1”的区分问题。

答案 3 :(得分:24)

选项1似乎是最好的,只要您的应用程序将分页视为生成相同资源的不同视图的技术。

话虽如此,URL方案相对无关紧要。如果您将应用程序设计为 hypertext-driven (因为所有REST应用程序必须按照定义),那么您的客户端将不会自己构建任何URI。相反,您的应用程序将提供指向客户端的链接,客户端将遵循它们。

您的客户可以提供的一种链接是分页链接。

所有这一切的令人愉快的副作用是,即使你改变了关于分页URI结构的思想并在下周实现完全不同的东西,你的客户也可以继续工作而不做任何修改。

答案 4 :(得分:11)

我一直使用选项1的样式。缓存并不是一个问题,因为在我的情况下数据经常变化。如果允许页面大小可配置,则无法再缓存数据。

我发现网址难以记住或不清洁。对我来说,这是一个很好的查询参数使用。资源显然是产品列表,查询参数只是告诉您希望列表显示的方式 - 排序和哪个页面。

答案 5 :(得分:8)

奇怪的是,没有人指出选项3具有特定顺序的参数。 HTTP //应用/产品/日期/降序/名称/升序/页/ 2 HTTP //应用/产品/名称/升序/日期/降序/页/ 2

指向相同的资源,但网址完全不同。

对我来说,选项1似乎是最可接受的,因为它明确区分了“我想要的”“我想要的”它(它们之间甚至有问号)大声笑)。可以使用完整URL实现整页缓存(无论如何,所有选项都会遇到同样的问题)。

使用Parameters-in-URL方法,唯一的好处是干净的URL。虽然你必须想出一些方法来编码参数并无损地解码它们。当然你可以使用URLencode / decode,但它会让网址变得丑陋:)

答案 6 :(得分:6)

我更喜欢使用查询参数offset和limit。

偏移:表示集合中项目的索引。

限制:用于商品数量。

客户可以按照以下方式继续更新偏移量

offset = offset + limit

下一页。

该路径被视为资源标识符。页面不是资源,而是资源集合的子集。由于分页通常是GET请求,因此查询参数最适合分页而不是标题。

我使用metamug。他们有这个可配置的。 Pagination on select query metamug

答案 7 :(得分:4)

我目前在ASP.NET MVC应用程序中使用类似于此的方案:

e.g。 http://application/products/by-date/page/2

具体来说是:http://application/products/Date/Ascending/3

但是,我不满意以这种方式在路由中包含分页和排序信息。

项目清单(本例中的产品)是可变的。即,下次有人返回包含分页和排序参数的URL时,他们获得的结果可能已经改变。因此,http://application/products/Date/Ascending/3作为一个指向已定义的,不变的产品集的唯一网址的想法将丢失。

答案 8 :(得分:3)

寻找我遇到此网站的最佳做法:

http://www.restapitutorial.com

在资源页面中有一个下载.pdf的链接,其中包含作者建议的完整REST最佳实践。除此之外还有一个关于分页的部分。

作者建议使用Range标头和使用查询字符串参数添加对两者的支持。

请求

HTTP标头示例:

Range: items=0-24

查询字符串参数示例:

GET http://api.example.com/resources?offset=0&limit=25

其中 offset 是开始项目编号, limit 是要返回的最大项目数。

<强>响应

响应应包含一个Content-Range标头,指示要返回的项目数以及尚未检索的项目总数

HTTP标头示例:

Content-Range: items 0-24/66

Content-Range: items 40-65/*

在.pdf中,针对更具体的案例还有其他一些建议。

答案 9 :(得分:1)

我倾向于同意slf“页面”不是真正的资源。另一方面,选项3更清晰,更易于阅读,并且可以更容易被用户猜到,甚至在必要时输入。我在选项1和3之间徘徊,但没有看到任何理由不使用选项3。

此外,虽然它们看起来不错,但正如有人提到的那样使用隐藏参数的一个缺点,而不是查询字符串或URL段,用户无法书签或直接链接到特定页面。根据应用程序的不同,这可能是也可能不是问题,但需要注意的事项。

答案 10 :(得分:0)

之前我使用过解决方案3(我写了很多django应用程序)。而且我认为它没有任何问题。它和其他两个一样可以生成(如果你需要做一些质量刮擦等),它看起来更干净。此外,您的用户可以猜测网址(如果它是一个面向公众的应用),而且人们喜欢能够直接去他们想要的地方,而且网址猜测会让人感到有能力。

答案 11 :(得分:0)

我在我的项目中使用以下网址:

http://application/products?page=2&sort=+field1-field2

这意味着 - &#34;给我页面按字段1升序排序的第二页,然后按字段2&#34;降序。或者,如果我需要更多的灵活性,我会使用:

http://application/products?skip=20&limit=20&sort=+field1-field2

答案 12 :(得分:0)

我使用以下模式来获取下一页记录。 http://application/products?lastRecordKey=?&pageSize=20&sort=ASC

RecordKey是在DB中保存顺序值的表的列。一次只能从DB提取一页数据。 pageSize用于确定要提取多少条记录。 sort用于对记录进行升序或降序排序。