You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

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继承,方法重写请求内容