【发布时间】:2010-10-29 19:06:55
【问题描述】:
我有兴趣向 JSON 文档集合公开直接 REST 接口(想想CouchDB 或Persevere)。我遇到的问题是如果集合很大,如何处理集合根上的GET 操作。
作为一个例子,假设我正在公开 StackOverflow 的 Questions 表,其中每一行都作为文档公开(不一定有这样的表,只是一个相当大的“文档”集合的具体示例)。该集合将在 /db/questions 上提供,通常的 CRUD api GET /db/questions/XXX、PUT /db/questions/XXX、POST /db/questions 正在使用中。获取整个集合的标准方法是 GET /db/questions,但如果这天真地将每一行作为 JSON 对象转储,您将获得相当大的下载量,并且服务器方面需要做大量工作。
解决方案当然是分页。 Dojo 已在其JsonRestStore 中解决了这个问题,方法是使用带有自定义范围单元items 的Range 标头的符合RFC2616 的巧妙扩展。结果是一个206 Partial Content,它只返回请求的范围。这种方法相对于查询参数的优势在于,它将查询字符串留给...查询(例如GET /db/questions/?score>200 或类似的,是的,它会被编码为%3E)。
这种方法完全涵盖了我想要的行为。问题是RFC 2616 在 206 响应中指定了这一点(强调我的):
请求必须包含 Range 标头字段 (section 14.35) 指示所需的范围,并且可能包含一个 If-Range 标头字段 (section 14.27) 以使请求有条件。
这在标头的标准用法的上下文中是有意义的,但这是一个问题,因为我希望 206 响应成为处理天真的客户/随机探索的人的默认值。
我已经详细阅读了 RFC 以寻找解决方案,但对我的解决方案不满意,并且对 SO 对这个问题的看法很感兴趣。
我的想法:
-
返回
200并带有Content-Range标头! - 我不认为这是错误的,但我更喜欢更明显的指示,表明响应只是部分内容。 -
Return
400 Range Required- 对于必需的标头没有特殊的 400 响应代码,因此必须使用默认错误并手动读取。这也使得通过网络浏览器(或其他一些客户端,如 Resty)进行探索变得更加困难。 - 使用查询参数 - 标准方法,但我希望允许查询坚持不懈,这会切入查询命名空间。
-
只需返回
206! - 我认为大多数客户不会惊慌失措,但我宁愿不违反 RFC 中的 MUST -
扩展规范!返回
266 Partial Content- 行为与 206 完全相同,但响应的请求不得包含Range标头。我认为 266 足够高,我不应该遇到碰撞问题,这对我来说很有意义,但我不清楚这是否被视为禁忌。
我认为这是一个相当普遍的问题,我希望看到这以一种事实上的方式完成,这样我或其他人就不会重新发明轮子。
当集合很大时,通过 HTTP 公开完整集合的最佳方式是什么?
【问题讨论】:
-
哇,这是一个很好的例子,说明之前已经进行了一些认真思考的问题。
-
就 Dojo 使用 Range 标头的方法而言,尽管 Accept-Ranges 允许扩展,但据我所知,Range 的 EBNF 不允许:tools.ietf.org/html/rfc2616#section-14.35.2。规范指出
Range = "Range" ":" ranges-specifier,其中tools.ietf.org/html/rfc2616#section-14.35.1 中的后者仅被描述为“byte-ranges-specifier”,它必须以“bytes-unit”开头,定义为字符串“bytes”。 -
Content-Range标头适用于正文(可用于上传大文件等时的请求,或下载时的响应)。Range标头用于请求某个范围。当请求中包含Range标头时,应使用206响应。如果不是,则响应可能仍包含Content-Range标头,但响应代码应为200。这个标头实际上似乎非常适合分页。 -
但是 RFC 2616 本身说“HTTP/1.1 实现可以忽略使用其他单位指定的范围。”那么使用 Range 标头进行分页是一种好习惯吗?因为它可能会损害互操作性。
标签: http rest http-headers pagination