web服务REST API版本控制有什么已知的方法或最佳实践吗?

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


当前回答

REST API的版本控制类似于任何其他API的版本。小的改动可以在适当的地方完成,大的改动可能需要一个全新的API。对您来说,最简单的方法是每次都从头开始,这意味着将版本放在URL中最有意义。如果你想让客户端的生活更轻松,你可以尝试保持向后兼容性,这可以通过弃用(永久重定向)、多个版本的资源等来实现。这需要更多的努力,也更麻烦。但这也是REST在“酷URI不改变”中鼓励的。

最后,它就像任何其他API设计一样。权衡努力与客户便利。考虑为您的API采用语义版本控制,这可以为您的客户明确新版本的向后兼容程度。

其他回答

我同意资源表示的版本控制更好地遵循REST方法。。。但是,自定义MIME类型(或附加版本参数的MIME类型)的一个大问题是,对HTML和JavaScript中的Accept和Content-Type头的支持较差。

例如,为了创建资源,IMO不可能使用HTML5格式的以下标头进行POST:

Accept: application/vnd.company.myapp-v3+json
Content-Type: application/vnd.company.myapp-v3+json 

这是因为HTML5 enctype属性是一个枚举,因此除了通常的application/x-www-formurlconted、multipart/form-data和text/plain之外的任何属性都是无效的。

…我也不确定HTML4中的所有浏览器都支持它(它具有更宽松的encytpe属性,但对于MIME类型是否被转发,这将是浏览器实现的问题)

因此,我现在觉得最合适的版本转换方式是通过URI,但我承认这不是“正确”的方式。

REST API的版本控制类似于任何其他API的版本。小的改动可以在适当的地方完成,大的改动可能需要一个全新的API。对您来说,最简单的方法是每次都从头开始,这意味着将版本放在URL中最有意义。如果你想让客户端的生活更轻松,你可以尝试保持向后兼容性,这可以通过弃用(永久重定向)、多个版本的资源等来实现。这需要更多的努力,也更麻烦。但这也是REST在“酷URI不改变”中鼓励的。

最后,它就像任何其他API设计一样。权衡努力与客户便利。考虑为您的API采用语义版本控制,这可以为您的客户明确新版本的向后兼容程度。

将您的版本放入URI中。API的一个版本并不总是支持来自另一个版本的类型,因此认为资源只是从一个版本迁移到另一个的说法是完全错误的。这与将格式从XML转换为JSON不同。类型可能不存在,或者它们可能在语义上发生了变化。

版本是资源地址的一部分。您正在从一个API路由到另一个API。在头中隐藏地址不是RESTful的。

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,发货对象是v4.2)。以下是您必须在客户端中提供的讨厌的URL:

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

有几个地方可以在REST API中进行版本控制:

如URI中所述。如果重定向等使用得当,这可能很容易处理,甚至在美学上也会令人愉悦。在Accepts:header中,因此版本在文件类型中。比如“mp3”和“mp4”。这也会起作用,尽管IMO的效果比。。。在资源本身中。许多文件格式的版本号都嵌入其中,通常在页眉中;这允许较新的软件通过理解文件类型的所有现有版本来“正常工作”,而如果指定了不受支持的(较新的)版本,则较旧的软件可以启动。在RESTAPI的上下文中,这意味着您的URI永远不需要更改,只需要您对所传递数据的特定版本的响应。

我可以看出使用这三种方法的原因:

如果您喜欢“清理”新的API,或者在需要这种方法的地方进行重大版本更改。如果您希望客户端在执行PUT/POST之前知道它是否可以工作。如果客户机必须执行PUT/POST以确定是否可以工作。