这是this question的更通用的重构(删除了Rails特定部分)
我不确定如何在RESTful Web应用程序中的资源上实现分页。
假设我有一个名为products
的资源,您认为以下哪种方法是最佳方法,以及原因:
例如。 http://application/products?page=2&sort_by=date&sort_how=asc
这里的问题是我不能使用整页缓存,而且URL也不是很干净且容易记住。
例如。 http://application/products/page/2?sort_by=date&sort_how=asc
在这种情况下,看到的问题是http://application/products/pages/1
不是唯一的资源,因为使用sort_by=price
会产生完全不同的结果和我仍然可以'使用页面缓存。
例如。 http://application/products/by-date/page/2
我个人认为使用这种方法没有问题,但有人警告我这不是一个好方法(他没有给出理由,所以如果你知道为什么它不是推荐,请告诉我)
任何建议,意见和批评都非常受欢迎。感谢。
答案 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用于对记录进行升序或降序排序。