有效的 URI(有时也称为更具体的术语 URL [1])是设计可用 HTTP API 的核心。统一资源标识符由 RFC 3986 定义,其在 HTTP 中的使用在 RFC 7230 中得到澄清。URI 是 API 中资源的标识符,也用于在网络上定位和寻址该资源。
URI 分为几个部分:scheme、authority、path、query 和 fragment(请参阅 RFC 3986 以获取更多详细信息)。作为 API 服务开发人员,path 和 query 最为相关;fragment 通常不会发送到服务,并且对 scheme 和 authority 的控制机会和理由也很少。
以下内容将仅关注 path 和 query。
注意
这远非详尽的列表。这仅仅是一个起点,我们可以从中积累有关如何构建良好 URI 的合理建议。
由于单个 URI 标识单个资源,因此在构建服务时,还需要保持以下两点:
任何资源应该只有一个 URI。在可能的情况下,不要提供多种引用相同资源的方式。使用 HTTP 重定向来解析间接请求到正确的规范 URI,并使用内容协商来请求不同的表示形式。
任何给定的资源,由于应该只有一个 URI,应该仅对该 URI 响应所有相关的 HTTP 方法,而不要为某些方法提供辅助 URI。例如
使用
GET /resources/1322b203bdc64c13b6e72b04d43e8690
DELETE /resources/1322b203bdc64c13b6e72b04d43e8690
绝不要
GET /resources/1322b203bdc64c13b6e72b04d43e8690
DELETE /resources/1322b203bdc64c13b6e72b04d43e8690/delete
通常,API 会有代表资源的集合的 URI,以及该集合中单个成员的层次结构。例如 [3]
GET /birds
{"birds": [
{
"name": "alpha",
"type": "crow"
},
{
"name": "beta",
"type": "jackdaw"
}]
}
GET /birds/alpha
{
"name": "alpha",
"type": "crow"
}
这样做是合理的,因为它有助于使 API 的元素易于理解。
如果使用上述层次结构,重要的是所有采用第二种形式的 URI(/birds/alpha)具有相同的语义,并且始终标识属于该集合的资源(在本例中“是一只鸟”)。
OpenStack 中有多个例子违反了这一概念。例如,在 nova 的 os-cells API 中,可以GET /os-cells获取单元列表,GET /os-cells/some-name获取名为some-name和GET /os-cells/details获取与GET /os-cells相同的信息,但包含更多详细信息。
对于这个特定的例子,一种方法(有几种选择)是在保持 URI 语义的同时,使用 query 来增强现有的集合 URI,以指示对更多详细信息的渴望 [3]
GET /os-cells?details=true
待办事项
关于如何指示布尔查询参数,目前尚存在未解决的争论。任何details=true, details=1或者details都有意义。上述内容不应被视为对任何 query 格式的支持。相反,它只是为了演示 URI 的 path 部分中更清晰的语义。
在某些情况下,可能需要返回与一组过滤条件匹配的资源集合。对于这些情况,使用 GET 方法,并创建一个将所有要求连接起来的查询字符串。例如,如果您需要返回所有蓝色且具有迁徙性且会游泳的鸟类,则 URI 将如下所示:
GET /birds?color=blue&migratory=true&swimming=true
URI 的长度限制因服务器和客户端而异。最严格的是一些浏览器,它们具有大约 2K 的最大 URI 长度,而像 Apache 这样的 Web 服务器将 URI 限制在约 8K 左右。如果表达请求的复杂需求所需的 URI 长度可能超过这些限制,可以使用POST方法,并将过滤条件传递到请求主体中。
脚注
| [1] | https://en.wikipedia.org/wiki/Uniform_Resource_Locator |
| [2] | 另一种观点认为,URI 应该是完全不透明的标识符,计算机使用它们来交换信息。这种观点很有价值,因为它允许标识符充当可互换的引用,但它低估了创建服务的各种客户端的价值和成本。如果我们希望鼓励这种多样化的集合,那么拥有易于人类理解的 URI 将有所帮助。 |
| [3] | (1, 2) 这些只是示例请求和响应,不应被视为明确描述正确的格式。 |