6.3 KiB
#RESTful API版本控制实施方案
1、为什么要做API版本控制
- 随业务发展,需求的更变,原有API逻辑处理,返回数据等的处理已不再适应于当前需求,改动在所难免。为避免API接口变化给使用者带来的影响,我们有需要区分不同版本的API接口处理
2、怎么做?
以下是主流的方式:
-
url路径携带版本号
例如: https://1hjz.3ncto.com.cn/v2/users
好处:能直观展示当前版本号。使用者能方便的调用不同版本API,直接看到效果
坏处:不优雅,违背RESTful架构原则,添加版本号会混淆版本和资源的概念,使得架构会比较混乱。增加日后维护成本
-
请求参数传递版本号
例如: https://1hjz.3ncto.com.cn/v2/users?version=v2.3.0
-
自定义请求头添加版本号(推荐)
在HTTP请求的header中使用自定义header标识版本。
例如:
Request Header Accept: application/json, text/javascript, */*; q=0.01 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Connection: keep-alive Host: disqus.com Content-Type: application/json version: 2.3.0好处: 遵循RESTful API设计风格。请求url不发生改变。
坏处:版本不直观,需要能设置header的client才能调用查看该API的效果。
3、实施方案
###3.1 原理
扩展SpringBoot配置。通过我们自定义请求映射处理器, 匹配比较执行相应方法上。
实现效果:当请求接口带有版本号时,通过版本号的比较,执行相应版本。假若当前版本有v2.1.0,v2.3.0。
-
不带版本号,执行默认版本。
-
请求version:1.1.0,找不到标识版本,将执行默认版本。
-
请求version:2.1.0,执行的是2.1.0,执行相应版本。
-
请求version:2.2.0,执行的是2.1.0,没有当前对应版本,将使用当前版本号下的最新版本。
-
请求version:3.2.0,执行的是2.3.0,没有当前对应版本,将使用当前版本号下的最新版本。
##3.2 java实现方案
项目目录结构,以1hjz为例
src.main.java
|-com.midou.tech
|- common
|- apiVersion API版本配置
|- modules
|- admin
|- controller
|- web
|- AdminBookController 原API接口文件
|- version
|- v2_3_0
|- AdminBookControllerV230 v2.3.0版本API接口文件
|- service
|- version
|- v230
|- impl
|- BookServiceImplV230 v2.3.0版本业务逻辑处理
|- BookServiceV230
|- impl
|- AdminBookServiceImpl
|- AdminBookService
3.2.1 实现自定义请求映射处理器
@Configuration
public class MyWebMvcRegistrations implements WebMvcRegistrations {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new ApiVersionRequestMappingHandlerMapping();
}
}
ApiVersionRequestMappingHandlerMapping
public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
return createCondition(apiVersion);
}
@Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
return createCondition(apiVersion);
}
private RequestCondition<ApiVersionCondition> createCondition(ApiVersion apiVersion) {
return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
}
}
3.2.2 实现自定义匹配比较器
/**
* 自定义匹配比较器
*/
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
/**
* 将不同筛选条件合并
* @param other
* @return
*/
@Override
public ApiVersionCondition combine(ApiVersionCondition other) {
...
}
/**
* 根据request查找匹配到的筛选条件
* @param request
* @return
*/
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
...
}
/**
* 不同筛选条件比较,用于比较
* @param other
* @param request
* @return
*/
@Override
public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
...
}
}
3.1.3 实现自定义注解@apiVersion
使用@apiVersion标识方法版本号。通过匹配规则的获取@apiVersion标识的版本号,比较筛选符合的方法
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {
String value() default "";
}
##4、使用
controller层
原API接口
@Api(description = "运营后台预约相关接口")
@RequestMapping(value = "/admin/web/book")
@RestController
public class AdminBookController extends AdminWebBaseController {
@GetMapping(value = "/card/list")
public JSONObject getServiceCardList() {
...
}
}
版本控制API命名
原controller名称+版本号
示例:AdminBookControllerV2_3_0
@Api(description = "运营后台预约相关接口")
@RequestMapping(value = "/admin/web/book")
@RestController
public class AdminBookControllerV2_3_0 extends AdminWebBaseController {
@ApiVersion("2.3.0")
@GetMapping(value = "/card/list")
public JSONObject getServiceCardList() {
...
}
}
service层
继承原接口层
命名规则: 原service名称+版本号
例如:当前bookService,2.3.0版本改变
public interface AdminBookServiceV2_3_0 extends AdminBookService {
...
}
实现类
@Primary
@Service
public class AdminBookServiceImplV2_3_0 extends AdminBookServiceImpl
implements AdminBookServiceV2_3_0 {
...
}
5、前端接口调用
"ajax": {
"url": url,
"type": 'GET',
"xhrFields": {// 允许跨域
withCredentials: true
},
"headers":{// API版本
"version": "2.3.0"
},
"crossDomain": true,
"data": function (d) {
...
}
}
问题:
controller继承,方法重写请求内容