BT

您是否属于早期采用者或者创新人士?InfoQ正在努力为您设计更多新功能。了解更多

如何版本化你的API?

| 作者 金灵杰 关注 1 他的粉丝 发布于 2017年9月12日. 估计阅读时间: 1 分钟 | 硅谷人工智能、机器学习、互联网金融、未来移动技术架构 ,尽在QCon上海2017

如何版本化API需要考虑各种实际业务场景,但是一个完备的API应该是:

  • 和客户端交互的约定。API需要确保稳定性,预先定义各种可能返回状态,包括各种异常。客户端无需考虑约定之外的情况。
  • 向下兼容。在API没有变化的时候,API实现的更新和升级,都应该确保原有客户端请求不出现问题。
  • RESTful。API设计应该能够遵照RESTful风格,通过URI来表示资源,通过HTTP GET、POST、PUT、DELETE等方法表示操作行为。

为了满足上述约定,版本化API不失为一种保持兼容性的好方法。版本化API的通常方式有:

URI中设置版本

这种方式通常在URI中增加一段用于标识版本,例如/v1/v2等。例如:

curl https://example.com/api/v2/lists/3  

这种方式的优势在于版本信息很容易明显的看出来,可以通过浏览器直接访问。

HTTP头中设置版本

这种方式的版本信息会放在HTTP的请求头中,通常会利用Accept字段,或者自定义一个字段。例如:

curl https://example.com/api/lists/3 \  
-H 'Accept: application/vnd.example.v2+json'

这种方式的好处是当版本升级时,URI保持不变,并且仅用于表示资源定位。

没有版本

版本化的目的是为了标识API的变化,如果API不会变化,或者每次都会重新扩展新的API,这种情况下,就可以标识版本信息。例如:

curl https://example.com/api/lists/3

一种折中方案

前面提到了三种版本化API的方式,通常情况下需要针对自己业务的特殊性来挑选其中的一种方式。但是,在实际应用场景中,情况会更加复杂,API的升级通常有两种情况:

  1. 大版本更新,例如字段类型变更、数据对象变更等。这种情况下无法满足对客户端的向下兼容,因此需要修改版本号。
  2. 小版本更新,例如增加可选参数、增加返回字段等。这种情况对于新客户端可以增加功能,对于老客户端仍然可以保持原有功能,可以不修改版本号。

因此,本文提出的折中方案是基于URI中的大版本号和HTTP头中的小版本号整合的方式。下面通过一个简单的示例来解释。

用户管理平台

一个常用的用户管理平台,提供以下API,通过用户ID获取用户信息:

curl https://example.com/api/v1/user/1
...
{
  "id": 1,
  "name": "test",
  "email": "test@example.com"
}

考虑以下两种变动情况:一种是用户id从数字变成了字符串,另一种是新增一个用户头像的值。

前者修改因为数据类型的变化,会导致客户端解析出现问题。因此这样的修改已经破坏了向下兼容性,此时就需要修改API的版本号。例如:

curl https://example.com/api/v2/user/1
...
{
  "id": "1",
  "name": "test",
  "email": "test@example.com"
}

第二种情况,对于旧客户端来说,只是增加了不使用的字段,通常的JSON格式解析库都可以忽略这些不使用的字段。对于新客户端则可以读取新的字段。例如:

curl https://example.com/api/v2/user/1
...
{
  "id": "1",
  "name": "test",
  "email": "test@example.com",
  "avatar": "http://example.com/1.jpg"
}

这种情况下,基本可以做到向下兼容,因此可以算是“小版本升级”。针对小版本升级,可以将小版本号放到HTTP头中。例如:

curl https://example.com/api/v2/user/1 \
    -H 'API-VERSION: 20170801'
...
{
  "id": "1",
  "name": "test",
  "email": "test@example.com",
  "avatar": "http://example.com/1.jpg"
}

后端路由

由于混合版本化的方式同时涉及到URI和HTTP头字段,前端代理(例如HAProxy、nginx)可以通过这些特定版本号字段将请求代理到对应的后端应用。

例如,前端使用HAProxy进行多版本分发,可以针对URI和HTTP头定制acl,然后再对这些acl进行组合,设置不同的backend。

acl is_v1 path_beg /api/v1
acl is_v2 path_beg /api/v2
acl is_version_1 hdr(API-VERSION) 20170801
acl is_version_2 hdr(API-VERSION) 20170701
use_backend old_server if is_v1 is_version_1
use_backend new_server if is_v2 is_version_2
backend old_server
...
backend new_server
...

这样可以将API版本化规则应用到不同的后端,以保证向下兼容性。

总结

基于本文版本化API规则,将“大版本”应用在URI上,将“小版本”应用在HTTP头字段上。通常来说,如果API升级之后破坏了向下兼容性,就应该升级“大版本”号;如果API升级可以向下兼容,可以升级“小版本”号。

版本化API有很多不同的设计方式,本文仅是其中一种。实际应用时,还是要根据业务场景进行选择,包括API版本升级频率,API稳定性等。通过HAProxy、nginx等代理服务,可以在确保向下兼容的情况下,由业务方决定老版本API的保留时间。


感谢郭蕾对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

为什么要将“小版本”应用在HTTP头字段 by 高 岩

既然返回的响应是向前兼容的,那么无论是否添加这个头字段响应都是一样的,客户端无需修改

Re: 为什么要将“小版本”应用在HTTP头字段 by 徐 xuchun

向下兼容,应该是在原来的基础上多加了返回字段。这样不影响原来的功能,又支持新的功能。

不错,挺好的实践分享,貌似OpenStack就是这么做的。 by 孙 庚泽

不错,挺好的实践分享,貌似OpenStack就是这么做的。

Re: 为什么要将“小版本”应用在HTTP头字段 by 高 岩

所以其实不添加也可以的
添加的目的是什么呢

Re: 为什么要将“小版本”应用在HTTP头字段 by 汤 辉

可以在不放弃大版本号的基础上,停用某个小版本的支持。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

5 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT