【问题标题】:How to implement robust pagination with a RESTful API when the resultset can change?当结果集可以更改时,如何使用 RESTful API 实现健壮的分页?
【发布时间】:2012-01-19 05:05:51
【问题描述】:

我正在实现一个 RESTful API,它将订单作为资源公开并支持通过结果集进行分页:

GET /orders?start=1&end=30

要分页的订单按ordered_at 时间戳排序,降序排列。这基本上是来自 SO 问题Pagination in a REST web application 的方法#1。

如果用户请求第二页订单(GET /orders?start=31&end=60),服务器只需重新​​查询订单表,再次按ordered_at DESC排序,返回位置31到60的记录。

我遇到的问题是:如果在用户查看记录时结果集发生变化(例如添加新订单),会发生什么情况?在添加新订单的情况下,用户会在结果的第二页的第一个位置看到旧订单 #30(因为现在相同的订单是 #31)。更糟糕的是,在删除的情况下,用户会在第二页(#31)的第一个位置看到旧订单 #32,而根本看不到旧订单 #31(现在是 #30)。

如果不以某种方式使 RESTful 服务器有状态 (urg) 或在每个客户端中构建一些分页智能,我就无法找到解决方案......有哪些成熟的技术可以处理这个问题?

为了完整性:我的后端是在 Scala/Spray/Squeryl/Postgres 中实现的;我正在构建两个前端客户端,一个在backbone.js 中,另一个在Python Django 中。

【问题讨论】:

    标签: scala rest backbone.js


    【解决方案1】:

    我这样做的方法是将索引从旧到新。所以他们永远不会改变。然后在没有任何开始参数的情况下查询时,返回最新的页面。此外,响应应该包含一个指示包含哪些元素的索引,因此您可以计算您需要为下一个旧页面请求的索引。虽然这不是您想要的,但对我来说似乎是最简单、最干净的解决方案。

    初始请求GET /orders?count=30 返回:

    {
      "start"=1039;
      "count"=30;
      ...//data
    }
    

    消费者由此计算出他想要请求:

    下一个请求: GET /orders?start=1009&count=30 然后返回:

    {
      "start"=1009;
      "count"=30;
      ...//data
    }
    

    除了原始索引,您还可以返回指向下一页的链接:

    {
      "next"="/orders?start=1009&count=30";
    }
    

    如果项目在中间插入或删除,则此方法会中断。在这种情况下,您应该使用一些自动递增的持久值而不是索引。

    【讨论】:

      【解决方案2】:

      可悲的事实是,从这个意义上说,我看到的所有网站都有“损坏”的分页,所以一定没有简单的方法来实现这一点。

      一个快速的解决方法可能是颠倒顺序,因此项目的位置是绝对的,并且不会随着新添加而改变。在您的首页,您可以提供最新的索引,以确保从那里进行一致的导航。

      • 优点:相同的 url 给出相同的结果
      • 缺点:没有明显的方法来获取最新元素...也许您可以使用负索引并将结果页面重定向到绝对索引。

      【讨论】:

        【解决方案3】:

        使用 RESTFUL API,应用程序状态应该在客户端。在这里,应用程序状态应该是某种时间戳或版本号,告诉您何时开始查看数据。在服务器端,您将需要某种形式的审计跟踪,它是正确的服务器数据,因为它不依赖于是否有客户端以及他们做了什么。至少,它应该知道数据上次更改的时间。这里与 REST 没有矛盾。

        您可以在 get 中添加版本参数。当客户端第一次需要页面时,它通常不会发送版本。服务器回复包含一个。例如,如果回复中有指向下一页/其他页面的链接,则这些链接包含 &version=... 客户端在需要另一个页面时应发送版本。

        当服务器收到带有版本的请求时,它至少应该知道自客户端开始查找以来数据是否发生了变化,并且取决于您拥有什么样的审计跟踪,它们是如何变化的。如果没有,它会正常应答,传输相同的版本号。如果他们有,它至少可以告诉客户。并且取决于它对数据如何变化的了解程度,它可能会相应地调整回复。

        举个例子,假设您收到一个包含 start、end、version 的请求,并且您知道由于 version 是最新的,因此 start 之前的 3 行已被删除。您可以使用 start-3、end-3、新版本发送重定向。

        【讨论】:

          【解决方案4】:

          WebSockets 可以做到这一点。您可以使用pusher.com 之类的东西来捕捉对数据库的实时更改并将更改传递给客户端。然后,您可以绑定不同的推送事件来处理模型和集合。

          【讨论】:

            【解决方案5】:

            只是要把它扔出去。请随时告诉我这是否完全错误以及为什么会这样。

            这种方法尝试使用 left_off 变量进行排序而不使用偏移量。

            考虑您需要使结果按时间戳 order_at DESC 排序。 所以当我要求第一个结果集时 这是

            SELECT * FROM Orders ORDER BY order_at DESC LIMIT 25;
            

            对吗? 当您请求第一页时就是这种情况(就 URL 而言,可能是没有任何内容的请求

            yoursomething.com/orders?limit=25&left_off=$timestamp

            那么当收到你的数据集时。只需获取上次查看项目的时间戳即可。 2015-12-21 13:00:49

            现在请求接下来的 25 个项目,请访问:yoursomething.com/orders?limit=25&left_off=2015-12-21 13:00:49(到最后查看的时间戳)

            在 Sql 中,您只需进行相同的查询并说出时间戳等于或小于 $left_off 的位置

            SELECT * FROM (SELECT * FROM Orders ORDER BY order_at DESC) as a 
            WHERE a.order_at < '2015-12-21 13:00:49' LIMIT 25;
            

            您应该从上次看到的项目中获得接下来的 25 个项目。

            对于那些看到这个答案的人。请评论这种方法是否相关甚至可能首先。谢谢。

            【讨论】:

              猜你喜欢
              • 2019-11-10
              • 1970-01-01
              • 1970-01-01
              • 2019-10-25
              • 1970-01-01
              • 2014-06-10
              • 1970-01-01
              • 1970-01-01
              • 2014-10-29
              相关资源
              最近更新 更多