在休息收藏中寻呼

我有兴趣将直接的REST接口公开给JSON文档集合(想想CouchDB或Persevere)。 我遇到的问题是如果集合很大,如何处理集合根上的GET操作。

作为一个例子,假设我公开了StackOverflow的Questions表,其中每一行都作为一个文档公开(并不是说这里必然存在这样一个表,只是大量“文档”集合的具体示例)。 使用通常的CRUD api GET /db/questions/XXXPUT /db/questions/XXXPOST /db/questions可以在/db/questions找到该集合。 获取整个集合的标准方法是GET /db/questions但如果它们将每行都天真地转储为JSON对象,那么您将获得相当大的下载量,并且在服务器方面会做很多工作。

当然,解决方案是分页。 Dojo在其JsonRestStore中通过使用Range标头和自定义范围单元items的聪明的符合RFC2616的扩展来解决了这个问题。 结果是206 Partial Content仅返回请求的范围。 这种方法在查询参数上的优点是,它留下了查询的查询字符串(例如GET /db/questions/?score>200或某些,并且是编码%3E )。

这种方法完全覆盖了我想要的行为。 问题在于RFC 2616指定了206响应(重点是我的):

请求必须包含一个Range头域(见14.35节),表示期望的范围,并且可以包含一个If-Range头域(见14.27节)以使该请求有条件。

这在标题的标准用法的上下文中是有意义的,但是这是一个问题,因为我希望206响应是处理朴素客户端/随机用户探索的默认值。

我详细介绍了RFC,寻找解决方案,但对我的解决方案感到不满,并对SO的解决方案感兴趣。

我有过的想法:

  • Content-Range标题返回200 ! - 我不认为这是错误的,但我更喜欢是否有一个更明显的指示,表明响应只是部分内容。
  • 返回400 Range Required - 对于所需的标题没有特殊的400响应代码,因此必须使用和手动读取默认错误。 这也使得通过网页浏览器(或Resty等其他客户端)更难探索。
  • 使用查询参数 - 标准方法,但我希望允许la Persevere查询,并切入查询命名空间。
  • 只需返回206 ! - 我认为大多数客户不会吓坏了,但我宁愿不要在RFC中反对
  • 扩展规格! 返回266 Partial Content - 完全像206一样,但是是响应一个不能包含Range标头的请求。 我认为266是足够高的,我不应该碰撞碰撞问题,这对我来说是有道理的,但我不清楚这是否被视为禁忌。
  • 我认为这是一个相当普遍的问题,我希望以某种事实的方式来看待这件事,所以我或其他人不会重新发明轮子。

    集合很大时,通过HTTP公开完整集合的最佳方式是什么?


    我的直觉是,HTTP范围扩展不是为你的用例而设计的,因此你不应该尝试。 部分响应意味着206 ,只有当客户端要求时才能发送206

    您可能需要考虑一种不同的方法,例如Atom中的一种使用方式(其中设计的表示可能是部分的,并且返回状态为200 ,并且可能包含分页链接)。 请参阅RFC 4287和RFC 5005。


    我不同意你们中的一些人。 我一直在为我的REST服务工作数周。 我最终做的很简单。 我的解决方案只能说明REST人员称为集合的意义。

    客户端必须包括一个“范围”来表示他需要收集的哪一部分,或者以其他方式准备好处理请求的413实体过大的误差时,要求收集过大的单次往返进行检索。

    服务器发送206 PARTIAL CONTENT响应,Content-Range报头指定资源的哪一部分已经发送,ETag报头用于标识集合的当前版本。 我通常使用类似Facebook的ETag {last_modification_timestamp} - {resource_id},并且我认为集合的ETag是它包含的最近修改的资源的ETag。

    要请求集合中的特定部分,则客户端必须使用“范围”报头,并且填补“如果 - 匹配”报头与来自先前执行的请求获得的集合的ETag的获取相同集合的其它部分。 服务器因此可以在发送所请求的部分之前验证集合没有改变。 如果存在更新版本,则会返回412 PRECONDITION FAILED响应,邀请客户端从头开始检索集合。 这是必要的,因为这可能意味着某些资源可能在当前请求的部分之前或之后添加或删除。

    我使用ETag / If-Match与Last-Modified / If-Unmodified-Since一起优化缓存。 浏览器和代理可能依靠其中的一个或两个用于他们的缓存算法。

    我认为一个URL应该是干净的,除非它包含一个搜索/过滤器查询。 如果你仔细想想,搜索只不过是集合的局部视图。 而不是汽车/搜索?q =宝马类型的URL,我们应该看到更多的汽车?制造商=宝马。


    如果有超过一页的回复,并且你不想一次提供整个系列,这是否意味着有多种选择?

    在对/db/questions的请求中,返回300 Multiple Choices具有Link标题的300 Multiple Choices ,指定如何访问每个页面以及具有URL列表的JSON对象或HTML页面。

    Link: <>; rel="http://paged.collection.example/relation/paged"
    Link: <>; rel="http://paged.collection.example/relation/paged"
    ...
    

    每个结果页面都有一个Link标题(空字符串表示当前URL,每个页面的URL相同,只是以不同范围访问),并且关系在每次即将到来时定义为一个自定义链接Link规范。 这种关系会解释你的习惯266 ,或者你违反206 。 这些头文件是您的机器可读版本,因为您的所有示例都需要理解客户端。

    (如果你坚持使用“范围”路由,我相信你自己的2xx返回代码,就像你描述的那样,这将是最好的行为,你应该为你的应用程序做这件事,并且这样的[“HTTP状态代码是可扩展的。“],你有很好的理由。)

    300 Multiple Choices ,你应该也应该为用户代理选择一种方式提供一个机构。 如果你的客户是理解的,它应该使用Link头。 如果是用户手动浏览,可能是一个HTML页面,其中包含指向可根据URL处理特定页面的特殊“分页”根资源的链接? /humanpage/1/db/questions或类似的可怕吗?


    Richard Levasseur的帖子上的评论让我想起了一个额外的选项: Accept头(14.1节)。 当oEmbed规范出来时,我想知道为什么它没有完全使用HTTP,并且写了一个使用它们的替代方案。

    保留300 Multiple ChoicesLink标头和HTML页面以获得最初的朴素HTTP GET ,但不使用范围,让您的新分页关系定义使用Accept标头。 您的后续HTTP请求可能如下所示:

    GET /db/questions HTTP/1.1
    Host: paged.collection.example
    Accept: application/json;PagingSpec=1.0;page=1
    

    Accept头允许你定义一个可接受的内容类型(你的JSON返回值),加上这个类型的可扩展参数(你的页码)。 在我的笔记上从我的oEmbed书写中跳过(不能链接到这里,我将它列在我的档案中),你可以非常明确地提供一个spec /关系版本,以防你需要重新定义page参数意味着未来。

    链接地址: http://www.djcxy.com/p/71439.html

    上一篇: Paging in a Rest Collection

    下一篇: What is the maximum size of a web browser's cookie's key?