原文:Pragmatic API Versioning
作者:Evan Larsson
翻译:Vincent
译者注:本文主要描述了几种API版本控制的方法。用户可以查询原始的API,或者添加定制的头文件来接收特定的版本。如果应用程序收到一个重大修订,将URI修改为V2。在进行迭代改进时,将创建与更改日期相一致的端点,并允许用户将日期信息附加。然后,可以选择保留旧版本的时间。而且在设计和版本化API时,您可以应用许多不同的理念。以下为译文
API设计是一个“火辣热门”的话题!关于API的最佳结构和版本的方法已经有很多优秀的文章介绍过了。在这篇文章中,我们将会深入研究不同的API设计之间有哪些冲突的地方,并在此基础上提出我们的中立观点,然后展示我们是怎样使用FLY去验证我们的中立观点的。
虽然没有一个统一的方式来设计API,但是有必要明确一下许多开发人员同意的几个关键想法。一个结构良好的Web API应该是…
1:与客户端保持持续的协议。协议可以保证一致性和稳定性;客户端应该可以使用API,而不用担心它会突然中断或消失。
2:更改或升级后向后兼容。对新端点的旧查询仍应产生预期的返回值。
3:RESTful(互联网应用程序)。它应当可以识别HTTP的相关动作 :GET,PUT,POST,PATCH,DELETE等。
为了最好地实现这些理想的要求,在如何实现不同版本的API上就是仁者见仁,智者见智了。我们来看看它们中的三个…
URI版本控制
curl https://example.com/api/v2/lists/3
通过解除URI中的版本号,客户端可以访问/v1/或/v2/API。它可读,适应性强,可直接插入用户浏览器。
Header版本控制
curl https://example.com/api/lists/3 \
-H 'Accept: application/vnd.example.v2+json'
API URI保持不变。头版本控制主要是通过自定义的Accept HTTP头来完成的。核心URI仍然保持不变,但是能最好表示出API的资源,并且版本的更改将通过头和响应类型传递。
没有版本控制!
curl https://example.com/api/lists/3
你需要什么版本?让我们扩展我们的API以适应新的或调整的案例!放弃旧的框架,选择建立和扩展。
每一个方法都是合理的,如果有好的设计思路的话,它们都可以呈现优异的API。 最终,我们希望它们都能实际运用起来,尽可能快地传输信息。一个没有版本控制的绿色API可能会变得混乱,所以我们将远离这一点。相反,我们将接受URI和HTTP标头中的版本控制。作为一个转折,我们将使用一个自定义的HTTP头。
为了避免出现连我们自己都无法了解为什么请求会如此混乱且冗长的情况出现,让我们更深入地了解一下为什么我们要使用这种方法。我们的方法是基于这样一句格言:随着太阳的升起,你的应用将会改变。
我们来看一个例子。
MightyList
我们正在为虚构应用程序MightyList构建API。在它简陋的早期,Mighty List允许用户创建列表。它具有从URI提供的API作为mightyapp.com/api/v1/。您可以使用API来请求列表信息,如下所示:
curl https://mightyapp.com/api/v1/lists/3
...
{
"listId": "3",
"shopping": "Shoes, tie, umbrella, snorkel",
"leisure": "Skiing, surfing, snorkeling ",
"food": "bananas, peanut butter, spinach",
"cost": "One hundred dollars"
}
在开发我们的应用程序时,我们可以期望更改两种类型:小版本调整和大版本调整。我们来看看一个小版本调整的例子,使用我们上面的示例响应,其中list 3的GET请求返回了shopping, leisure, food和cost的字符串值。开发团队希望调整数据模型,cost现在变成了整数而不是字符串了。
curl https://mightyapp.com/api/v1/lists/3
...
{
"listId": "3",
"shopping": "Shoes, tie, umbrella, snorkel",
"leisure": "Skiing, surfing, snorkeling ",
"food": "bananas, peanut butter, spinach",
"cost": 100
}
此更改会破坏我们API的向后兼容性!如果有人将MightyList API应用到他们的应用程序中,那么接收一个整数而不是字符串可能会导致他们的应用程序中断。为了避免出现这种意外的情况,任何破坏我们向后兼容性的微小变化都需要对新版本进行bump version。
另一个需要考虑的情况是大版本调整:MightyList版本2正在进行中。lists将成为superlists,我们也添加了很多新的资源:bots,ai——等等许多奇特的东西。这个重大升级肯定会对兼容性产生重大影响; 这是一个全新的应用!
这两个示例都揭露了在改进API时如何保持URL是最新的难度。小版本修改是否会破坏我们的URL版本?这样最终会有很多版本。我们希望保持向后兼容性,但随着应用的不断发展,它将变得越来越困难。
在我们讨论这个问题之前,让我们定义一个安全的和向后兼容的API更改应该是什么样子的:
添加新资源。
添加新的事件类型。
为响应添加新的属性。
改变属性的顺序。
向现有方法添加可选参数。
当通过选项或全新的资源或事件参数添加新事物时,您可完全不用担心与客户端建立的协议,因为它非常稳定,也不会产生任何变化。他们可以继续访问API资源,也会得到预期的响应。如果我们更改了现有资源或预期的响应内容,我们需要在API版本中把这种更改给展示出来。
为了使我们的应用程序的状态清晰,我们希望在URI中有一个表示基本产品版本的版本;当您的产品从根本上改变时,URI版本将会更改。 MightyList V1使用/ api / v1 /。 MightyList V2使用/ api / v2 /。
为了使我们的API的当前版本清晰,我们将使用自定义的HTTP头来表示较小的修订。这类似于让用户请求应用程序版本V2.x。X是API的版本。
在两个地方进行版本控制是一种实用的做法;您的应用程序将迭代,并且总是有可能变成更大的东西。我们来看看Fly如何轻松地做到这一点。
在Fly里面,每个站点都由多个后端组成。您可能在GitHub上有一个静态页面,应用程序使用了Kubernetes集群或Heroku部署,并且还用了一个或多个数据库对数据进行管理。
构建API时,您可以通过对API进行解耦,并将其作为自己的后端主机,这样API的可扩展性和负载平衡的优势就会得到改善。对API解耦也使得版本控制变得相对容易一点。
首先,我们将为最初的API/api/v1/添加一个新的后端。 我们将指定相关路径去匹配/api/v1/。如果有多个冗余实例需要负载平衡,我们可以将它们视为独立的后端进行添加,设置相同的路径,然后调整优先级。如果想要实现这种想法,我们可以使用Fly Middleware根据设备或地理信息,将用户路由到各种API后端。
上面我们创建了一个基线API端点。我们还希望为API V1的最新版本创建一个后端。我们将以04-05-2017为终点。在此之后,我们可以设置API路由规则。
现在,要为第二个后端配置FLY路由规则,以便让那些附加了自定义头文件的用户可以收到最新版本的API。我们将使用头名称API-Version,因为它能让用户一眼看出其含义,让用户容易记住,也比较清晰。APIAPI-Version头文件将是年月日或04-05-2017的形式。这样做将允许我们为API创建一个公共的变更日志。用户可以通过更改日期来获取他们需要的端点。
太令人兴奋了!我们已经将API-version设置为04-05-2017,如果头文件中出现了这些信息,用户就会被路由到API V1 - 04-05-2017的后端。用户现在可以选择请求API的特定时间点,同时保留从example.com/api/v1 URI获取原始API V1的能力。您可以查看我们第一个API后端的原始路由规则:
让我们回顾一下:不同的API有不同的端点。用户可以查询最初的API,或者添加定制的头文件来接收特定的版本。如果应用程序进行了一次大版本的调整,我们将正式地将URI修改为V2。
在进行迭代改进时,我们也会按照修改的日期同时创建一个端点,并允许用户将日期头文件信息附加到上面。然后,我们可以选择旧版本可以保留多久,之后在不考虑向后兼容性以及不影响用户使用的情况下,可以选择合适的时间删除它们。
在设计API版本时,您可以应用许多不同的理念。使用本文中的实例,您可以看到基于Fly头文件或URI后端路由的这些灵活性,可以让你实现关于“PI体系结构和版本控制方案”最疯狂的梦想。