【问题标题】:When do I use path params vs. query params in a RESTful API?何时在 RESTful API 中使用路径参数与查询参数?
【发布时间】:2015-09-07 04:31:40
【问题描述】:

我想让我的 RESTful API 非常可预测。决定何时使用 URI 而不是使用查询参数对数据进行分段的最佳做法是什么?

对我来说,支持分页、排序和分组的系统参数位于“?”之后是有意义的。但是像“状态”和“区域”这样的字段或其他划分您的收藏的属性呢?如果这些也是查询参数,那么知道何时使用路径参数的经验法则是什么?

【问题讨论】:

标签: rest spring-mvc restful-url restful-architecture api-design


【解决方案1】:

在 REST API 中,您不应过度关注可预测的 URI。 URI 可预测性的建议暗示了对 RESTful 架构的误解。它假定客户端应该自己构建 URI,而他们实际上不应该这样做。

但是,我假设您创建的不是真正的 REST API,而是“受 REST 启发”的 API(例如 Google Drive 之一)。在这些情况下,经验法则是“路径参数 = 资源标识”和“查询参数 = 资源排序”。那么,问题就变成了,您能否在没有状态/区域的情况下唯一标识您的资源?如果是,那么它可能是一个查询参数。如果不是,那么它是一个路径参数。

【讨论】:

  • 我不同意,好的 API 应该是可预测的; RESTful 或其他。
  • 我想是的。 URI 的形成方式应该有韵律和理由,而不是任意命名端点。当一个人可以在不经常参考文档的情况下直观地编写 API 客户端时,我认为你已经编写了一个很好的 API。
  • “当人们可以直观地编写 API 客户端而不需要经常参考文档时”。这就是我认为我们对 REST 的理解不同的地方...... API 客户端永远不需要“构建”一个 URL。他们应该从上一个 API 调用的响应中选择它。如果您将网站作为类比...您访问 facebook.com,然后选择指向活动页面的链接。您不在乎 facebook 事件 URL 是否“可预测”,因为您没有输入它。您可以通过超媒体链接到达那里。 REST api 也是如此。因此,使 URI 对您(服务器)有意义,但对客户端没有意义
  • 添加注释。这并不意味着 URI 不应该遵循易于理解的模式,它只是意味着它不是 RESTful API 的约束。这个领域最大的问题是人们假设客户端应该自己构建 URL。他们不应该,因为这会在客户端和服务器之间创建不应该存在的耦合。 (例如,服务器不能在不破坏所有客户端应用程序的情况下更改 URL)。在 REST API 中,服务器可以随意更改它们。
  • +1 用于使用以下词:“'路径参数 = 资源标识'和'查询参数 = 资源排序'”。这真的让我明白了。
【解决方案2】:

RESTful API 设计的最佳实践是路径参数用于标识一个或多个特定资源,而查询参数用于对这些资源进行排序/过滤。

这是一个例子。假设您正在为名为 Car 的实体实现 RESTful API 端点。你可以像这样构建你的端点:

获取/cars
获取/cars/:id
发布/cars
/cars/:id
删除/cars/:id

这样,您仅在指定要获取的资源时使用路径参数,但这不会以任何方式对资源进行排序/过滤。

现在假设您想在 GET 请求中添加按颜色过滤汽车的功能。因为颜色不是资源(它是资源的属性),所以您可以添加执行此操作的查询参数。您可以像这样将该查询参数添加到您的 GET /cars 请求中:

获取/cars?color=blue

将实施此端点,以便只返回蓝色汽车。

就语法而言,您的 URL 名称应全部小写。如果您的实体名称通常是英文中的两个单词,您将使用连字符来分隔单词,而不是驼峰式。

例如。 /two-words

【讨论】:

  • 感谢您的回答,迈克。这是一种清晰而简单的方法;值得我投赞成票。尽管如此,开发人员经常选择“汽车/蓝色”方法,我想知道他们这样做的理由是什么......也许他们决定为必填字段制作路径参数,或者他们这样做是为了表明数据库由该分片分区。
  • 我不确定他们的推理是什么。老实说,我不同意。我认为遵循惯例并保持简单是最有意义的。通过这样做,您可以让 API 的使用者更好地了解他们需要做什么才能访问其功能。
  • 如何使用 /cars?id=1&color=blue 而不是 cars/1/?color=blue。您基本上是在每个场景中过滤汽车资源
  • 错误,因为 ID 为 1 的汽车只存在一辆,而蓝色的汽车可能很多。有身份和过滤器的区别
  • 有趣的琐事,this-is-called-kebab-case
【解决方案3】:

一般来说,当资源中有明显的“层次”时,我倾向于使用路径参数,例如:

/region/state/42

如果该单一资源具有状态,则可以:

/region/state/42/status

但是,如果“区域”实际上不是所公开资源的一部分,它可能属于查询参数之一 - 类似于分页(如您所提到的)。

【讨论】:

    【解决方案4】:

    示例网址: /rest/{keyword}

    此 URL 是路径参数的示例。我们可以通过@PathParam获取这个URL数据。

    示例网址: /rest?keyword=java&limit=10

    此 URL 是查询参数的示例。我们可以通过@Queryparam获取这个URL数据。

    【讨论】:

      【解决方案5】:

      分段更具层次性和“漂亮”,但可能会受到限制。

      例如,如果您有一个包含三个部分的 url,每个部分传递不同的参数以通过品牌、型号和颜色搜索汽车:

      www.example.com/search/honda/civic/blue
      

      这是一个非常漂亮的 url,更容易被最终用户记住,但现在你有点卡在这个结构上了。假设您想让用户在搜索中搜索所有蓝色汽车或所有本田思域?查询参数解决了这个问题,因为它提供了一个键值对。所以你可以通过:

      www.example.com/search?color=blue
      www.example.com/search?make=civic
      

      现在您可以通过它的键来引用该值 - 查询代码中的“颜色”或“制作”。

      您可以通过可能使用更多段来创建一种键值结构来解决这个问题,例如:

      www.example.com/search/make/honda/model/civic/color/blue
      

      希望这是有道理的..

      【讨论】:

        【解决方案6】:

        思考这个问题的基本方法如下:

        URI 是唯一标识资源类型的特定实例的资源标识符。就像生活中的其他一切一样,每个对象(它是某种类型的实例)都具有一组时不变或临时的属性。

        在上面的示例中,汽车是一种非常有形的物体,具有品牌、型号和 VIN 等属性 - 永远不会改变,而颜色、悬架等可能会随着时间而改变。因此,如果我们使用可能随时间(时间)变化的属性对 URI 进行编码,我们最终可能会得到同一个对象的多个 URI:

        GET /cars/honda/civic/coupe/{vin}/{color=red}
        

        多年后,如果这辆车的颜色变成黑色:

        GET /cars/honda/civic/coupe/{vin}/{color=black}
        

        请注意,汽车实例本身(对象)没有改变 - 只是颜色发生了变化。让多个 URI 指向同一个对象实例将迫使您创建多个 URI 处理程序——这不是一种有效的设计,当然也不直观。

        因此,URI 应该只包含永远不会改变的部分,并且将在其整个生命周期内继续唯一地标识该资源。所有可能发生变化的东西都应该保留给查询参数,例如:

        GET /cars/honda/civic/coupe/{vin}?color={black}
        

        底线 - 考虑多态性。

        【讨论】:

        • 有趣的范式..这是常用的设计模式吗?您能否提供一些在其文档中使用此功能的 API 或概述此策略的一些参考资料?
        • 我喜欢您在写“URI 是唯一标识资源类型的特定实例的资源标识符”时强调“类型”的方式。我认为这是一个重要的区别。
        • 这是最有意义的。我认为路径变量有助于使参数更清晰、更易于理解。
        • 在 REST-API 设计中这是一个非常好的观点和规则:URI should only consist of parts that will never change and will continue to uniquely identify that resource throughout its lifetime
        【解决方案7】:

        我设计了一个主要资源为people 的API。通常用户会请求过滤people,所以为了防止用户每次都调用/people?settlement=urban之类的东西,我实现了/people/urban,后来我可以轻松添加/people/rural。这也允许访问完整的/people 列表,如果以后有任何用处。简而言之,我的推理是为公共子集添加路径

        来自here

        常见查询的别名

        为了让普通消费者的 API 体验更愉快,请考虑将条件集打包到易于访问的 RESTful 路径中。比如上面最近关闭的ticket查询可以打包成GET /tickets/recently_closed

        【讨论】:

          【解决方案8】:

          考虑一下“路径”这个词 - 一种到达某个位置的方式。路径参数应描述如何到达您感兴趣的位置/资源。这包括目录、ID、文件等。

          /vehicles/cars/vehicle-id-1
          

          这里,vehicle-id-1 是一个路径参数。

          考虑“查询”这个词 - 我认为它是询问有关路径的问题,即我的路径是蓝色的,我的路径是否有 100 个结果。

          /vehicles/cars/vehicle-id-1?color=blue&limit=100
          

          这里的color=bluelimit=100 是查询参数,它们有助于描述我们在获得资源后应该做什么:过滤掉蓝色的,并将它们限制为100 个结果。

          【讨论】:

            猜你喜欢
            • 2022-01-11
            • 2020-01-16
            • 1970-01-01
            • 1970-01-01
            • 2022-11-29
            • 1970-01-01
            • 2021-11-15
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多