API的版本控制是一个有争议的主题,您会在互联网上找到许多相互矛盾的指南。最常用的模式可能是在URL的路径段中带有版本标识符的模式。由于在版本控制方面几乎没有共识,因此仅提供有关该主题的意见可能不会很有帮助,但我们提供了两种想法:
1.完全不进行API版本控制是一种明智的方法,越来越受到关注;
2.链接和版本同时出现在url中
我们都知道API需要演变,并且在不破坏客户端的情况下演变API是一个挑战,因此似乎不感到奇怪的是,不做任何处理是版本控制的明智选择。 在我们的API中,我们注意到,我们通常首先将
“/v1”放在URL中作为路径段,以预见更改,而从未更改过。 为什么是这样?
可以使用向后兼容的方式对API进行许多更改。如果您可以通过向后兼容的方式进行更改而无需更改版本,则应该这样做。 通常,只要客户提前知道可能会发生这种情况,将新属性添加到API中是安全的。您可以在以下方面做些事情。
1)一种是通过向服务器上的资源添加一些随机属性来帮助客户端进行测试。这并非难事,不会损害您的API,并且可以让您的客户确保当资源中出现一两个额外的属性时,他们不会受到影响。您可以做的另一件事是使用PATCH而不是PUT进行更新。 PUT的语义是它完全替代了资源的状态。这意味着客户端必须包括他们正在更新的所有资源的所有属性,甚至包括编写客户端时不存在的属性。这给客户端造成负担,并使服务器本身容易受到客户端错误的影响。
2)另一方面,PATCH仅更改客户端显式引用的数据。 服务器负责合并,这比较安全。当然,您永远不应该实现具有PATCH语义的PUT操作-尽管我们已经看到这样做不止一次。通常,您也可以在不破坏现有客户端的情况下向API引入新的资源类型。 如果在旧数据中可能会出现对新资源类型的引用,则在断开旧客户端时要格外小心,但是您通常可以避免这种情况。
您将不时地对数据模型进行重构。 没有任何版本控制技巧可以以不破坏旧客户端的方式进行这样的更改-您将需要实现新的API和某种针对旧数据的迁移策略。这些变化是痛苦的,希望很少发生,但是除非您的API发展不大,否则它们将必然发生。 许多主要的家喻户晓的网络公司在其API中都经历了至少一次这样的过渡。
在API中包含版本控制的想法取决于这些极端事件(1.这些更改太大,无法以完全向后兼容的方式进行,2.但是这些更改不足以要求全新API)之间存在一系列更改的假设,实际上,许多人发现他们的API根本不存在这种更改类别-我们至少可以说这已经发生在我们身上。
有时您会看到它指出必须从一开始就在API中包含版本控制,因为以后无法添加。根据我们的经验,这是不正确的。 如果您发布不带版本控制的API并想在以后添加它,则无论您将版本标识符放在URL还是标题中,这通常都很容易。 缺少版本标识符的请求被视为V1请求。因为很容易请稍后再添加,并且由于通常不需要这样做,因此我们认为,如果您不确定如何对新API进行版本控制,则可以放心地删除它。
如果您确实决定在API中包括版本控制,则可能会面临将版本标识符放在URL或标头中的选择。如果您还使用链接,则会发现将版本信息放在标头中是更简单的选择。 下面是原因:
GET /v2/dogs/12345678 HTTP/1.1
Host: dogtracker.com
…
HTTP/1.1 200 OK
{
“sel”: “https://dogtracker.com/v2/dogs/12345678”,
“id”: “1234567”,
“kind”: “Dog”
“name”: “Lassie”,
“furColor”: “brown”,
“owner”: “https://dogtracker.com/v2/persons/98765432”
}
让我们回到前面使用的示例:
「在此示例中,当服务器产生链接https://dogtracker.com/v2/persons/98765432时,它正在猜测要在URL中包含哪个版本。 由于客户要求提供小狗Lassie的V2版本,因此可以假设客户想要的是Duke的V2? Duke是一个人-人们甚至拥有V2版本吗? 还有一个令人不愉快的概念问题-从概念上讲,将版本作为具有URL的资源是可以的,但是Lassie不是被版本所拥有,而是一个人所拥有。
(In this example, when the server produced the link https://dogtracker.com/v2/persons/98765432, it was making a guess on which version to include in the URL. Since the client asked for V2 of Lassie the Dog, maybe it is safe to assume that V2 of the person the Duke of Rudling is what the client will want? The Duke is a person—do people even have a V2 format? There is also an unpleasant conceptual problem—it is conceptually OK for versions to be resources that have URLs, but Lassie is not owned by a version, she is owned by a person.)」
解决这两个问题的一种方法是发明1 + n个URL,一个用于Duke自己,另一个用于每个版本。Duke本人的URL用于链接,客户端可以根据需要将其转换为版本URL。这解决了服务器的问题,该服务器现在可以始终返回Duke的网址,但这会增加客户端从Duke的URL创建版本URL的负担。程序员必须查阅带外文档,以学习形成版本URL的模式-不能从数据中推论得出。 OpenStack开源项目的API使用这种方法。
我们知道的一个银行组织也采用这种方法,通过返回相对URL,使客户的工作变得容易一些,如下所示:
GET /v2/dogs/12345678
HTTP/1.1
Host: dogtracker.com
…
HTTP/1.1 200 OK
{
“self”: “https://dogtracker.com/v2/dogs/12345678”,
“id”: “12345678”,
“kind”: “Dog”
“name”: “Lassie”,
“furColor”: “brown”, “owner”: “/persons/98765432”
}
此处返回的Duke的URL是相对的-如果我们解决了该URL,则为https://dogtracker.com/persons/98765432,因为它相对的基础是https://dogtracker.com/v2/dogs/12345678。 由于该URL以相对形式返回,因此客户端可以很容易地在https://dogtracker.com/v2或https://dogtracker.com/v1之前添加他们想要的版本的URL。 这个前置操作不是标准的URL解析操作,但它是一个简单的字符串操作,这是大多数程序员关心的。 您可以决定https://dogtracker.com/persons/98765432上的GET结果应该是什么。
如您所见,将版本标识符放入URL可以与链接一起使用,但是与将版本标识符放入Accept-Version或类似的标头中相比,它带来了更多的概念和实践复杂性。两种方法都需要使用必须在文档中传达的知识对定制客户端进行编程。 当然,不实施版本控制仍然更加简单,并且避免了需要专门知识的文档。