Web服务REST API版本控制是否有任何已知的方法或最佳实践?
我注意到AWS通过端点的URL进行版本控制.这是唯一的方法还是有其他方法来实现同一目标?如果有多种方式,每种方式的优点是什么?
这是一个很好而且棘手的问题.URI设计的主题同时也是REST API中最突出的部分,因此可能是对该API用户的长期承诺.
由于应用程序的演变以及在较小程度上它的API是生活中的事实,并且它甚至类似于看似复杂的产品(如编程语言)的演变,因此URI设计应该具有较少的自然约束并且应该保留一段时间.应用程序和API的生命周期越长,对应用程序和API用户的承诺就越大.
在另一方面,生活的另一个事实是,这是很难预见将通过API消耗的所有资源及其方面.幸运的是,这是没有必要的设计将用于直到整个API 启示.正确定义每个资源和资源实例的所有资源端点和寻址方案就足够了.
随着时间的推移,你可能需要新的资源和新的属性添加到每个特定的资源,但API遵循用户访问特定资源的方法不应改变,一旦资源寻址方案成为公众,因此决赛.
此方法适用于HTTP动词语义(如PUT应该总是更新/更换),而且在早期版本的API支持HTTP状态代码(他们应该继续工作,这样无需人工干预工作过的是API客户端应能继续工作像那样).
此外,由于嵌入的API版本到URI会扰乱这个概念超媒体作为应用状态的引擎由具有资源地址/ URI会随时间而改变(在罗伊T. Fieldings博士论文说),我可以得出结论,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/xmlNeil Armstrong
标题包含包含您要求的表示的行("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+xmlNeil Armstrong
此外,假设客户认为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类型(或附加版本参数的MIME类型)的一个大问题是在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,但我接受它不是'正确'的方式.
将您的版本放在URI中.API的一个版本并不总是支持另一个版本的类型,因此资源仅从一个版本迁移到另一个版本的论点是完全错误的.这与将格式从XML切换到JSON不同.这些类型可能不存在,或者它们可能在语义上发生了变化.
版本是资源地址的一部分.您正在从一个API路由到另一个API.在标题中隐藏寻址并不是RESTful.
您可以在REST API中进行版本控制的一些地方:
如上所述,在URI中.如果使用重定向等,这可以是易处理的,甚至是美学上令人愉悦的.
在Accepts:标题中,因此版本在filetype中.喜欢'mp3'vs'mp4'.这也可行,虽然IMO的工作效果不如......
在资源本身.许多文件格式都嵌入了它们的版本号,通常在标题中; 这允许更新的软件通过理解文件类型的所有现有版本来"正常工作",而较旧的软件可以在指定不受支持的(较新的)版本时发挥作用.在REST API的上下文中,这意味着您的URI永远不必更改,只需要您对所交付的特定数据版本的响应.
我可以看到使用这三种方法的原因:
如果您喜欢"干净扫描"新API,或者您希望采用这种方法进行主要版本更改.
如果您希望客户端在进行PUT/POST之前知道它是否能够正常工作.
如果客户端必须执行其PUT/POST以确定它是否能够正常工作,那就没关系了.
对REST API进行版本控制类似于任何其他API的版本控制.可以进行微小的更改,主要更改可能需要一个全新的API.最简单的方法是每次都从头开始,这就是将版本放入URL中最有意义.如果您想让客户端更轻松,您可以尝试保持向后兼容性,可以使用弃用(永久重定向),多个版本的资源等.这更加繁琐,需要更多努力.但它也是REST鼓励"酷URI不改变"的东西.
最后,它就像任何其他API设计一样.权衡客户的便利性.考虑为您的API采用语义版本控制,这使您的客户清楚地知道新版本的向后兼容性.