我想让我的 RESTful API 非常可预测。决定何时使用 URI 而不是使用查询参数对数据进行分段的最佳实践是什么?
对我来说,支持分页、排序和分组的系统参数在“?”之后是有意义的。但是像“状态”和“区域”这样的字段或其他划分您的收藏的属性呢?如果这些也是查询参数,那么知道何时使用路径参数的经验法则是什么?
RESTful API 设计的最佳实践是路径参数用于标识特定资源或资源,而查询参数用于对这些资源进行排序/过滤。
这是一个例子。假设您正在为名为 Car 的实体实现 RESTful API 端点。您将像这样构建端点:
获取 /cars
获取 /cars/:id
发布 /cars
放置 /cars/:id
删除 /cars/:id
这样,您仅在指定要获取的资源时使用路径参数,但这不会以任何方式对资源进行排序/过滤。
现在假设您想在 GET 请求中添加按颜色过滤汽车的功能。因为颜色不是资源(它是资源的属性),所以您可以添加执行此操作的查询参数。您可以像这样将该查询参数添加到您的 GET /cars
请求中:
获取/cars?color=blue
将实施此端点,以便只返回蓝色汽车。
就语法而言,您的 URL 名称应全部小写。如果您的实体名称通常是英文中的两个单词,您将使用连字符来分隔单词,而不是驼峰式。
前任。 /two-words
思考这个问题的基本方法如下:
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}
底线 - 考虑多态性。
URI should only consist of parts that will never change and will continue to uniquely identify that resource throughout its lifetime
在 REST API 中,您不应该过度关注可预测的 URI。 URI 可预测性的建议暗示了对 RESTful 架构的误解。它假定客户端应该自己构建 URI,而他们实际上不应该这样做。
但是,我假设您不是在创建一个真正的 REST API,而是一个“受 REST 启发”的 API(例如 Google Drive 之一)。在这些情况下,经验法则是“路径参数 = 资源标识”和“查询参数 = 资源排序”。那么,问题就变成了,您能否在没有状态/区域的情况下唯一标识您的资源?如果是,那么它可能是一个查询参数。如果不是,那么它是一个路径参数。
分割更具层次性和“漂亮”,但可能会受到限制。
例如,如果您有一个包含三个部分的 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
希望这是有道理的..
/cars
是一种资源。也许如果您有不同的车库出售汽车,那么 /cars/[garage]/
是一种资源。然后,您可以搜索所有汽车的品牌和颜色,或者在车库中搜索品牌和颜色。
一旦我设计了一个主要资源是 people
的 API。通常用户会请求过滤的 people
,因此,为了防止用户每次都调用 /people?settlement=urban
之类的东西,我实现了 /people/urban
,它后来使我能够轻松添加 /people/rural
。如果以后有任何用处,这也允许访问完整的 /people
列表。简而言之,我的推理是为公共子集添加路径
从 here:
常见查询的别名 为了让普通消费者的 API 体验更愉快,请考虑将条件集打包到易于访问的 RESTful 路径中。例如,上面最近关闭的门票查询可以打包为 GET /tickets/recently_closed
考虑一下“路径”这个词——一种到达某个位置的方式。路径参数应该描述如何到达您感兴趣的位置/资源。这包括目录、ID、文件等。
/vehicles/cars/vehicle-id-1
这里,vehicle-id-1
是一个路径参数。
考虑“查询”这个词 - 我认为它是询问有关路径的问题,即我的路径是蓝色的,我的路径是否有 100 个结果。
/vehicles/cars/vehicle-id-1?color=blue&limit=100
这里的 color=blue
和 limit=100
是查询参数,它们有助于描述我们在获得资源后应该做什么:过滤掉蓝色的,并将它们限制为 100 个结果。
一般来说,当资源中存在明显的“层次结构”时,我倾向于使用路径参数,例如:
/region/state/42
如果该单一资源具有状态,则可以:
/region/state/42/status
但是,如果“区域”实际上不是所公开资源的一部分,则它可能属于查询参数之一-类似于分页(如您所述)。
示例网址: /rest/{keyword}
此 URL 是路径参数的示例。我们可以使用 @PathParam
获取此 URL 数据。
示例网址: /rest?keyword=java&limit=10
此 URL 是查询参数的示例。我们可以使用 @Queryparam
获取此 URL 数据。
this-is-called-kebab-case