【问题标题】:RESTful API sorting dilemmaRESTful API 排序困境
【发布时间】:2014-04-23 21:10:49
【问题描述】:

我已经实现了 REST API,遵循的原则是 rest 只返回基本文档和这些文档中的 refs 如何获取其他内容等

例如 /car/5 会给我 model:blabla , user_id: 1 然后如果你需要所有者,你会得到 /user/1 来获取用户数据..

这种方式在 DB 中避免了 JOINS 和其他东西。所有东西都在它们自己之间链接,并且在其余客户端部分互连数据 - 让事情变得简单,易于缓存/删除缓存、缩放等。

但是当您需要排序时会发生什么?

假设我们在前端有一些视图来显示以下数据: 汽车型号、用户名等...例如,您想按用户名排序。

您不能真正告诉 /car/5 按用户名排序,因为它只知道用户 ID...

我看到的一个选项是从用户 /user/list?sortby=username 排序,然后互连这些返回的 id 中的哪些实际上是指汽车。但这意味着我们需要获得所有用户......并且只使用那些看似致命的性能瓶颈的一小部分。

感谢大家的指点

【问题讨论】:

    标签: architecture sorting rest


    【解决方案1】:

    我认为您正试图避免出于所有错误的原因加入,因为采用这种方法,您将不得不处理更多的请求。

    如果您取而代之的是带回您要显示的所有信息(即连接数据库端),那么客户端将不得不进行更少的查询,而您可以进行排序无需(懒惰)加载所有子对象。

    另一种选择是将子对象作为子 XML 元素带回(与 OpenStreetMap 的 RESTful 接口的工作方式类似)。您仍然有来自客户端的一次点击,您应该能够优化查询以最小化数据库上的负载。

    【讨论】:

      【解决方案2】:

      如果您要避免服务器中的所有连接并将其留给客户端,那么在这种情况下,您也必须将排序留给客户端。这是因为必须进行连接才能按用户名对汽车进行排序。

      您可以创建另一个名为 CarsAndOwners 或类似的资源,它返回加入的列表,此时在服务器上进行排序变得合理。

      【讨论】:

        【解决方案3】:

        首先,您的示例 /car/5 应该是资源的复数版本,以便 RESTful:

        /cars/5
        /users/1
        

        现在,一般来说,如果您想要一种 RESTful 方法进行排序,您可以这样做:

        /cars?sort=make&order=asc
        

        在您的示例中,您提到要按用户名对汽车进行排序。但是,就像您提到的那样,cars 集合应该对users 一无所知。因此,如果您想显示汽车列表并按用户分组,您会怎么做?您需要一种方法来检索此信息。一种方法是遍历用户并请求与每个用户关联的汽车:

        # pseudo code
        
        users.each do |user|
          # assuming the user id is 5,
          # to display the user's cars, 
          # we need to request /users/5/cars
        end
        

        因此,我们可以通过/users/5/cars 之类的 URL 请求每个人的汽车列表。这称为嵌套资源。我们只请求与用户 5 相关的汽车。

        但是,您可能会遇到向您的 API 发出大量请求的问题(根据您的用例,这可能没问题)。

        另一种可能更好的方法是在一个请求中执行此操作:

        /users/cars?sort=username&order=asc
        

        这将返回一组用户及其按用户名排序的汽车。

        另一种方法可能是:

        /users?include=[cars]&sort=username&order=asc
        

        但我不像以前那样喜欢这种方法。我会推荐以前的方法/users/cars

        附注:您的列表应该分页,这样当您只显示部分列表时,就不会产生请求所有 users 的开销。

        /users?page=1&per_page=50
        

        【讨论】:

        • 复数资源命名不是 REST 的要求——要求是为每个资源提供统一的语义:意思是,每个暴露的资源应该以相同的方式(从消费者的角度)表现可用的协议方法(GET、POST、DELETE 等)
        【解决方案4】:

        我首先想象我想编写的理想 RESTful 查询,例如:

        /car/list?sortby=username
        

        然后弄清楚如何实现它。

        现在您的 list 方法为您提供了 car 对象,这些对象不会像您所说的那样公开 username ,但我认为这并不重要.在准备好列表之前,所有的排序都应该在服务器上进行。

        为了做到这一点,您需要在某个时候将您的 car 对象加入到 user 对象中,并且没有办法回避这个问题。您唯一的选择就是在哪里做。

        如果您正在使用传统的 RDBMS,那么在数据库级别执行此操作是有意义的。


        您是否在应用程序 API 和数据库之间使用 ORM 层?

        【讨论】:

        • 好吧,所有的持久性都在 REST 之后。目前休息正在使用 ORM,但我认为这无关紧要。
        • 我得到了你在使用 ORM 的提示,因为你说你在避免连接数据库,并在 REST 应用层连接你的所有对象。我可以看到这种简单干净的架构的吸引力,但这可能是您当前困境的根源。为了让应用程序正常工作,我希望对这种架构做出妥协:在 DB 上存储额外的存储过程或视图,返回预先排序的数据,仍然为这些对象提供 ORM 映射,并让 REST 应用程序在方法之间进行选择取决于查询。
        【解决方案5】:

        数据库的设计目的是快速进行连接,因此将其留给客户端或您的服务器应用程序对我来说没有意义。

        我在 REST 上读到的几乎所有内容都表明实体/资源之间的关系也应该是一种资源。因此,您应该有一个代表该加入/关系的资源 /mydomain.com/CarsAndUsers。

        然后您可以像其他任何资源一样对该资源进行排序,它将由数据库处理:

        /mydomain.com/CarsAndUsers?sortby=用户名

        您也可以像任何其他资源一样对该资源(和分页)应用过滤。

        编辑:

        根据 cmets,最好这样做:

        /cars?embed=user,sortby=用户名

        感谢 Eric 指出这一点。

        【讨论】:

        • 这是 API 端点组合爆炸的秘诀。一种更常见的 RESTful 方法是使用参数来请求内联对象(本质上是连接表示)。例如:/albums?embed=artist。这些可用于嵌入的资源可以在超媒体links 集合中包含链接,以使选项可被发现。
        • 感谢 Eric,虽然我同意您关于端点爆炸的观点,但问题不在于相关对象,而在于根据相关集的属性对一组进行排序。我不确定您的建议是否可以解决该特定问题。或者您是说 OP 可以这样做:/cars?embed=user,sortby=username
        • 是的,这正是我的建议。
        • @EricElliott 感谢您的确认,是的,您是对的,这是一种更好的方法。我将编辑我的答案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多