良好的API需要保持向前兼容,特别是在APP场景或者是其他第三方客户端的场景,产品需求的升级改进要求不影响现有的版本正常工作。因此对外暴露的所有API需要有统一的版本管理策略来应对兼容性问题。
根据不同的应用场景,制定了两种API的版本控制模式: 1. API(URL)自带版本
https://www.fota.com/api/account/user/general?_t=1541397626404
https://www.fota.com/api/v1.2/account/user/general?_t=1541397626404
https://www.fota.com/api/v1.3/account/user/general?_t=1541397626404
2. HTTP Header指定版本
:authority: fota.com
:method: GET
:path: /api/account/user/general?_t=1541402217410
:scheme: https
accept: application/json, text/plain
accept-encoding: gzip, deflate, br
api-version:1.0.1
开闭原则。 对于小版本的更新可以在单个接口中进行处理,对于大版本的更新,可以提供新的Controller,或新建服务部署新版本的接口,保留每个版本的服务。
1. 小版本升级 小版本的更新,在原接口中做扩展,做兼容。例如:一个应用场景,在1.0.1版本是获取的是总资产和保证金率的数据,在1.0.2版本获取的是总资产和安全边界的数据,在后续的版本获取的是保证金率和安全边界的数据。
@Authorization
@RequestMapping(value = "/general", method = RequestMethod.GET)
@ResponseBody
public Result getUserGeneralInfo() {
String apiVersion = RequestHeaderContext.getInstance().getApiVersion();
if (apiVersion.equals("1.0.1")){
// 获取总资产和保证金率数据
} else if (apiVersion.equals("1.0.2")){
// 获取总资产和安全边界数据
} else {
// 获取保证金率和安全边界数据
}
Long userId = tokenUtil.getUserIdByLoginToken(RequestHeaderContext.getInstance().getToken());
return accountManager.getUserGeneralInfo(userId);
}
当然,服务端也可以做兼容,把总资产、保证金率和安全边界的数据都返回,保障返回的JSON对象字段只增不删不改,客户端根据自己当前的版本显示不同的值。 apiVersion的值是从Header获取:
:authority: fota.com
:method: GET
:path: /api/contract/list?contractId=1027&_t=1541402217410
:scheme: https
accept: application/json, text/plain
accept-encoding: gzip, deflate, br
api-version:1.0.1
2. 大版本升级 无法兼容的接口,采用新建Controller,甚至部署新的应用服务和nginx。例如:这次这个接口需要获取的数据是一个List的数据,而不是两个单独的值。
把修改的接口放在新的Controller中,旧的接口不需要处理,客户端自己区分。
@Slf4j
@Controller
@RequestMapping("v1.2/account/user")
public class AccountControllerV2 extends BaseController {
@Autowired
private AccountManager accountManager;
/**
* 总资产和保证金率的List数据
*
* @return
*/
@Authorization
@RequestMapping(value = "/general", method = RequestMethod.GET)
@ResponseBody
public Result getUserGeneralInfo() {
String apiVersion = RequestHeaderContext.getInstance().getApiVersion();
if (apiVersion.equals("1.2.1")){
// 获取总资产和保证金率数据
} else if (apiVersion.equals("1.2.2")){
// 获取总资产和安全边界数据
} else {
// 获取保证金率和安全边界数据
}
Long userId = tokenUtil.getUserIdByLoginToken(RequestHeaderContext.getInstance().getToken());
return accountManager.getUserGeneralInfo(userId);
}
}
在服务端新建Controller,甚至部署新的应用服务和nginx(方便回滚)。
https://www.fota.com/api/account/user/general?_t=1541397626404
https://www.fota.com/api/v1.2/account/user/general?_t=1541397626404
https://www.fota.com/api/v1.3/account/user/general?_t=1541397626404
1. 接口变动非常大或者整个产品大版本发布 此种情况下可以采用URL自带版本的方式,提供新的Controller,甚至部署新的应用服务和nginx。URL中无版本号即走默认逻辑。
2. 常规的版本升级和BUGFIX 一般情况下使用HTTP Header中指定的版本号,在代码逻辑中进行判断就可满足需求。Header中无版本号即走默认处理逻辑。
3. 两种模式同时使用 URL自带模式用来处理大版本变动,当大版本已经升级完成,后续的小需求迭代仍然可以使用HEADER的方式来保持API兼容。