一.前言
一个web项目中,随着需求的变更或增加,API接口也会跟着变化,而如果APP发布后,已使用的接口肯定不能直接覆盖更新,需要新增升级版本接口和新的APP版本对应,因此多个版本接口更替后,如何更优雅管理不同版本接口代码,如何设计更直观的接口文档呈现给app端,这是我们后端工程师需要考虑的事情,下面总结接口版本管理经验。
二.API版本规范
考虑到接口今后一定会进行版本迭代,因此一开始开发的时候,就需要对代码进行版本考量下的代码目标架构。
1. 控制器目录架构:
v1版本控制器目录:apps/controller/v1/……
v2版本控制器目录:apps/controller/v2/……
在controller下增加子集文件夹:controller/v1/……、controller/v2/……等等,初始版本的接口全部放在v1下。
2. 接口设计
2.0.利用域名
- 不同版本使用不同域名,v1.api.amap.com,v2.api.amap.com
2.1.利用URL path
- 请求URL path区分管理 同一域名,api.amap.com/v1/,api.amap.com/v2/
- 不同版本控制器代码分类依据
-
在项目发布前的所有代码,都属于
v1
版本代码; -
项目发布后,如果是新增加的需求,和已发布v1版本接口需求不冲突,新增控制器或接口,仍然属于
v1版本代码
; -
项目发布后,如果新增需求和v1已发布版本接口
冲突
,为了兼容老版本APP,必须在v2文件夹
下新增控制器开发v2版本接口。
-
遵循以上分类原则,依次递推,严格区分不同版本接口代码,结合下一部分的接口文档,利于不同版本接口更新维护。
2.3. 利用自定义的request header
HTTP GET:
http://haveibeenpwned.com/api/breachedaccount/foo
api-version: 2
2.4.利用content type
http://haveibeenpwned.com/api/breachedaccount/foo
Accept: application/vnd.haveibeenpwned+json; version=2.0
2.5.利用URL里的parameter
- 根据请求参数区分管理 同一域名,请求参数中带version=v1/v2/v3等,然后识别具体版本执行对应方法
HTTP GET:
http://haveibeenpwned.com/api/breachedaccount/foo?version=v2
三.API版本升级方案约定和案例
开闭原则:对于小版本的更新可以在单个接口中进行处理
,对于大版本的更新,可以创建新的Controller
,或新建服务部署新版本的接口,保留每个版本的服务。
1. 小版本升级
小版本的更新,在原接口中做扩展,做兼容。
- 例如:一个应用场景,在
1.0.1
版本是获取的是总资产和保证金率的数据,在1.0.2版本获取的是总资产和安全边界的数据,在后续的版本获取的是保证金率和安全边界的数据。
@RequestMapping(value = "/general", method = RequestMethod.GET)
@ResponseBody
public Result getUserGeneralInfo() {
//获取请求头版本号
String version = RequestHeaderContext.getInstance().getApiVersion();
if (version.equals("1.0.1")){
// 获取总资产和保证金率数据
} else if (version.equals("1.0.2")){
// 获取总资产和安全边界数据
} else {
// 获取保证金率和安全边界数据
}
return 返回Result ;
}
2. 大版本升级
无法兼容的接口,采用新建Controller
,甚至部署新的应用服务和nginx
。
- 例如:这次这个接口需要获取的数据是一个
List的数据
,而不是两个单独的值。- 把修改的接口放在新的Controller中,旧的接口不需要处理,客户端自己区分。
@Slf4j
@Controller
@RequestMapping("v1.2/account/user")
public class AccountControllerV2 extends BaseController {
@Autowired
private AccountManager accountManager;
/**
* 总资产和保证金率的List数据
*/
@RequestMapping(value = "/general", method = RequestMethod.GET)
@ResponseBody
public Result getUserGeneralInfo() {
String version= RequestHeaderContext.getInstance().getApiVersion();
//获取请求头版本号
if (version.equals("1.2.1")){
// 获取总资产和保证金率数据
} else if (version.equals("1.2.2")){
// 获取总资产和安全边界数据
} else {
// 获取保证金率和安全边界数据
}
return Result返回List数据;
}
}
四.总结
1. 接口变动非常大或者整个产品大版本发布
- 此种情况下可以采用URL自带版本的方式,创建新的Controller,甚至部署新的应用服务和nginx。URL中无版本号即走
默认
逻辑。
2. 常规的版本升级和BUGFIX
- 一般情况下使用
HTTP Header
中指定的版本号,在代码逻辑
中进行判断就可满足需求**。Header中无版本号**即走默认
处理逻辑`。
3. 两种模式同时使用
- URL自带模式用来处理
大版本变动
,当大版本已经升级完成,后续的小需求迭代仍然可以使用HEADER
的方式来保持API兼容。