Web服务REST API版本是否存在任何已知的操作方法或最佳做法?
我注意到,AWS通过端点的URL进行版本控制。这是唯一的方法还是有其他方法可以实现相同的目标?如果有多种方法,每种方法的优点是什么?
Web服务REST API版本是否存在任何已知的操作方法或最佳做法?
我注意到,AWS通过端点的URL进行版本控制。这是唯一的方法还是有其他方法可以实现相同的目标?如果有多种方法,每种方法的优点是什么?
Answers:
这是一个很好且棘手的问题。URI设计主题同时是REST API的最重要部分,因此,对于该API的用户可能是长期的承诺。
由于应用程序的发展以及在较小程度上其API的存在是生活中的事实,并且它甚至与看似复杂的产品(如编程语言)的发展相似,因此URI设计应具有较少的自然约束,并且应予以保留随着时间的流逝。应用程序和API的寿命越长,对应用程序和API用户的承诺就越大。
另一方面,生活的另一个事实是,很难预见将通过API消耗的所有资源及其各个方面。幸运的是,直到Apocalypse才需要设计整个API 。正确定义所有资源端点以及每个资源和资源实例的寻址方案就足够了。
随着时间的流逝,您可能需要向每个特定资源添加新资源和新属性,但是一旦资源寻址方案公开并最终确定,API用户访问特定资源所遵循的方法就不应更改。
此方法适用于HTTP谓词语义(例如,PUT应该始终更新/替换)和早期API版本所支持的HTTP状态代码(它们应继续工作,以便在没有人工干预的情况下工作的API客户端应能够继续工作)像那样)。
此外,由于将API版本嵌入URI将通过使资源地址/ URI随时间变化而破坏作为应用程序状态引擎的超媒体的概念(在Roy T. Fieldings博士论文中指出),因此我得出结论,API版本不应长时间保存在资源URI中,这意味着API用户可以依赖的资源URI应该是永久链接。
当然,可以在基本URI中嵌入API版本,但只能用于合理和受限的用途,例如调试与新API版本一起使用的API客户端。这种版本化的API应该是有时间限制的,并且仅对有限的一组API用户可用(例如在封闭Beta中)。否则,您将自己奉献到不应该的地方。
关于维护具有过期日期的API版本的一些想法。通常用于实现Web服务的所有编程平台/语言(Java,.NET,PHP,Perl,Rails等)都允许将Web服务端点轻松绑定到基本URI。这样,很容易收集并保持跨不同API版本的文件/类/方法的集合。
从API用户POV来看,只要很明显但仅在有限的时间内(即在开发过程中),就可以更轻松地使用和绑定到特定的API版本。
从API维护者的POV出发,通过使用主要以文件(源代码)版本控制的最小单位工作的源代码控制系统,可以更轻松地并行维护不同的API版本。
但是,在URI中清晰可见的API版本中,有一个警告:由于URI设计中的API历史变得可见/可见 ,因此很可能会随着时间的推移而发生变化,这与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标头结合使用,HTTP标头重定向到资源URI的适当版本,该版本仍为:
http:// shonzilla / api / customers / 1234
至少有两个适用于API版本控制场景的重定向HTTP状态代码:
301已永久移动,指示带有请求URI的资源已永久移动到另一个URI(应该是不包含API版本信息的资源实例永久链接)。此状态代码可用于指示过时/不受支持的API版本,通知API客户端已将版本化的资源URI替换为资源永久链接。
302找到,指示请求的资源临时位于另一个位置,而请求的URI可能仍受支持。当无版本URI暂时不可用,并且应该使用重定向地址(例如,指向嵌入了APi版本的URI)重复请求并且我们要告诉客户端继续使用它时,此状态代码可能很有用。永久链接)。
其他场景可以在HTTP 1.1规范的Redirection 3xx一章中找到
410 Gone
,因为重定向可能表示新位置兼容,否则不兼容。如果该API只是已过时而仍然存在,Warning
则可以使用Response上的HTTP标头。
该网址不应包含版本。该版本与您所请求资源的“想法”无关。您应该尝试将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>
标题包含包含所要表示的行(“接受: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等,如公认的答案所示。
永远保持向后兼容性通常是成本过高和/或非常困难的。我们希望提前通知弃用,此处建议的重定向,文档以及其他机制。
我同意对资源表示进行版本控制更好地遵循REST方法...但是,自定义MIME类型(或附加了version参数的MIME类型)的一个大问题是对HTML和HTML中的Accept和Content-Type标头的支持不佳JavaScript。
例如,为了创建资源,IMO不可能使用HTML5格式的以下标头进行POST:
Accept: application/vnd.company.myapp-v3+json
Content-Type: application/vnd.company.myapp-v3+json
这是因为HTML5 enctype
属性是一个枚举,因此任何其他比平常application/x-www-formurlencoded
,multipart/form-data
并且text/plain
是无效的。
...我也不确定HTML4中的所有浏览器都支持该功能(它的encytpe属性较为宽松,但是对于是否转发MIME类型将是浏览器的实现问题)
因此,我现在觉得最合适的版本控制方法是通过URI,但我接受这不是“正确”的方法。
在REST API中可以在几个地方进行版本控制:
如前所述,在URI中。如果正确使用重定向之类的方法,这可能很容易处理,甚至在审美上也令人愉悦。
在Accepts:标头中,因此版本位于文件类型中。就像“ mp3”和“ mp4”一样。这也将起作用,尽管IMO的效果不如...
在资源本身中。许多文件格式通常在标头中嵌入其版本号;这样可以通过了解文件类型的所有现有版本来使较新的软件“正常运行”,而如果指定了不受支持的(较新的)版本,则较旧的软件可以正常运行。在REST API的上下文中,这意味着您的URI不必更改,而只需您对所处理数据的特定版本的响应即可。
我可以看到使用这三种方法的原因: