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 等,正如接受的答案所暗示的那样。
永远保持向后兼容通常是成本过高和 / 或非常困难的。我们更愿意提前通知弃用,重定向此处建议,文档和其他机制。