协慌网

登录 贡献 社区

API 版本控制的最佳实践?

Web 服务 REST API 版本控制是否有任何已知的方法或最佳实践?

我注意到AWS 通过端点的 URL 进行版本控制 。这是唯一的方法还是有其他方法来实现同一目标?如果有多种方式,每种方式的优点是什么?

答案

这是一个很好而且棘手的问题。 URI 设计的主题同时也是 REST API 中最突出的部分 ,因此可能是对该 API 用户的长期承诺

由于应用程序的演变以及在较小程度上它的 API 是生活中的事实,并且它甚至类似于看似复杂的产品(如编程语言)的演变,因此URI 设计应该具有较少的自然约束并且应该保留一段时间 。应用程序和 API 的生命周期越长,对应用程序和 API 用户的承诺就越大。

另一方面,生活中的另一个事实是很难预见通过 API 消耗的所有资源及其方面。幸运的是,没有必要设计将在Apocalypse之前使用的整个 API。正确定义每个资源和资源实例的所有资源端点和寻址方案就足够了。

随着时间的推移,您可能需要为每个特定资源添加新资源和新属性,但是一旦资源寻址方案变为公共且因此最终,API 用户访问特定资源所遵循的方法不应更改。

此方法适用于 HTTP 动词语义(例如,PUT 应始终更新 / 替换)和早期 API 版本中支持的 HTTP 状态代码(它们应继续工作,以便在没有人为干预的情况下工作的 API 客户端应该能够继续工作像那样)。

此外,由于将 API 版本嵌入到 URI 中会破坏超媒体作为应用程序状态引擎的概念(在 Roy T. Fieldings 博士论文中阐述),因为资源地址 / URI 会随着时间的推移而变化,我会得出结论API 版本不应长时间保存在资源 URI 中,这意味着API 用户可以依赖的资源 URI 应该是永久链接

当然, 可以在基本 URI 中嵌入 API 版本,仅限于合理和受限制的用途,例如调试与新 API 版本一起使用的 API 客户端 。此类版本化 API 应该是有时间限制的,并且仅限于有限的 API 用户组(例如在封闭的测试版中)。否则,你会把自己投入到你不应该做的地方。

有关维护 API 版本的一些想法,这些 API 版本具有到期日期。通常用于实现 Web 服务(Java,.NET,PHP,Perl,Rails 等)的所有编程平台 / 语言允许将 Web 服务端点轻松绑定到基 URI。通过这种方式,可以轻松收集并保持 不同 API 版本之间的文件 / 类 / 方法集合。

从 API 用户 POV 开始,当它显而易见但仅在有限的时间内(即在开发期间)时,它也更容易使用并绑定到特定的 API 版本。

从 API 维护者的 POV 中,通过使用主要处理文件的源控制系统作为(源代码)版本控制的最小单元,可以更容易地并行维护不同的 API 版本。

但是,在 URI 中清晰可见的 API 版本中有一个警告:人们也可能反对这种方法,因为API 历史在 URI 设计中变得可见 / 明显 ,因此容易随着时间的推移而发生变化,这违反了 REST 的指导原则。我同意!

解决这个合理异议的方法是在无版本 API 基 URI 下实现最新的 API 版本。在这种情况下,API 客户端开发人员可以选择:

  • 针对最新版本进行开发(承诺维护应用程序,使其免受可能破坏其设计糟糕的 API 客户端的最终 API 更改)。

  • 绑定到 API 的特定版本(变得明显),但仅限于有限的时间

例如,如果 API v3.0 是最新的 API 版本,则以下两个应该是别名(即行为与所有 API 请求相同):

http://shonzilla/api/customers/1234
http://shonzilla/api /v3.0 /customers/1234
http://shonzilla/api /v3 /customers/1234

此外, 如果他们使用的 API 版本已过时或不再受支持,则应通知仍尝试指向 API 的 API 客户端使用最新的先前 API 版本。因此,访问任何过时的 URI,如下所示:

http://shonzilla/api /v2.2 /customers/1234
http://shonzilla/api /v2.0 /customers/1234
http://shonzilla/api /v2 /customers/1234
http://shonzilla/api /v1.1 /customers/1234
http://shonzilla/api /v1 /customers/1234

应该返回任何指示重定向30x HTTP 状态代码,这些代码Location HTTP 标头一起使用,重定向到仍然是这个的资源 URI 的适当版本:

http://shonzilla/api/customers/1234

至少有两个适用于 API 版本控制方案的重定向 HTTP 状态代码:

  • 301 永久移动,指示具有请求的 URI 的资源永久移动到另一个 URI(应该是不包含 API 版本信息的资源实例永久链接)。此状态代码可用于指示过时 / 不受支持的 API 版本,通知 API 客户端已将版本化资源 URI 替换为资源永久链接

  • 302 Found表示所请求的资源暂时位于另一个位置,而仍可支持请求的 URI。当无版本的 URI 暂时不可用并且应该使用重定向地址重复请求时(例如,指向嵌入了 APi 版本的 URI)并且我们想要告诉客户端继续使用它(即固定链接)。

  • 其他方案可以在HTTP 1.1 规范的重定向 3xx 章节中找到

URL 不应包含版本。该版本与您请求的资源的 “想法” 无关。您应该尝试将 URL 视为您想要的概念的路径 - 而不是您希望项目返回的方式。版本规定了对象的表示,而不是对象的概念。正如其他海报所说,你应该在请求标题中指定格式(包括版本)。

如果您查看具有版本的 URL 的完整 HTTP 请求,它看起来像这样:

(BAD WAY TO DO IT):

http://company.com/api/v3.0/customer/123
====>
GET v3.0/customer/123 HTTP/1.1
Accept: application/xml

<====
HTTP/1.1 200 OK
Content-Type: application/xml
<customer version="3.0">
  <name>Neil Armstrong</name>
</customer>

标题包含包含您要求的表示的行(“Accept:application / xml”)。那是版本应该去的地方。每个人似乎都掩盖了这样一个事实,即你可能想要不同格式的相同的东西,并且客户应该能够询问它想要什么。在上面的示例中,客户端要求资源的任何 XML 表示 - 实际上并不是它想要的真实表示。理论上,服务器可以返回与请求完全无关的内容,只要它是 XML 并且必须进行解析才能意识到它是错误的。

更好的方法是:

(GOOD WAY TO DO IT)

http://company.com/api/customer/123
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+xml

<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+xml
<customer>
  <name>Neil Armstrong</name>
</customer>

此外,假设客户认为 XML 太冗长,现在他们想要 JSON。在其他示例中,您必须为同一客户提供新的 URL,因此您最终会得到:

(BAD)
http://company.com/api/JSONv3.0/customers/123
  or
http://company.com/api/v3.0/customers/123?format="JSON"

(或类似的东西)。实际上,每个 HTTP 请求都包含您要查找的格式:

(GOOD WAY TO DO IT)
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+json

<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+json

{"customer":
  {"name":"Neil Armstrong"}
}

使用这种方法,您可以更自由地设计,并且实际上遵循 REST 的原始概念。您可以在不中断客户端的情况下更改版本,也可以在 API 更改时逐步更改客户端。如果您选择停止支持表示,则可以使用 HTTP 状态代码或自定义代码响应请求。客户端还可以验证响应的格式是否正确,并验证 XML。

还有许多其他优点,我在我的博客上讨论了其中的一些优点: http//thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

最后一个示例显示如何将版本放入 URL 中。假设您想要在对象内部获得一些信息,并且您已经对各种对象进行了版本化(客户是 v3.0,订单是 v2.0,而 shipto 对象是 v4.2)。这是您必须在客户端中提供的令人讨厌的 URL:

(Another reason why version in the URL sucks)
http://company.com/api/v3.0/customer/123/v2.0/orders/4321/

我们发现将版本放在 URL 中是实用且有用的。它可以让您轻松了解您正在使用的内容。我们使用别名 / foo 到 / foo /(最新版本)以便于使用,更短 / 更清洁的 URL 等,正如接受的答案所暗示的那样。

永远保持向后兼容通常是成本过高和 / 或非常困难的。我们更愿意提前通知弃用,重定向此处建议,文档和其他机制。