资讯详情

API版本控制-自定义RequestMappingHandlerMapping实现

1.为什么要使用版本控制?

系统不断优化,功能不断增加,现有的业务逻辑不能满足用户的需求,必须迭代版本,界面必须改进,但改进不能影响以前的业务,需要迭代版本,迭代版本需要控制版本。

2.版本控制的几种方式

1、域名区分管理,即不同版本使用不同的域名, v1.api.test.com,v2.api.test.com

2、请求url 在同一域名下使用不同的路径区分url路径,test.com/api/v1/,test.com/api/v2

3.区分请求参数,在同一条线上url在路径下,增加version=v1或v2 等待,然后根据不同的版本选择执行不同的方法。

4.请求在头中区分版本,在同一个版本url路径下,请求头中增加版本号version:1或version:2.然后根据不同的版本选择执行不同的方法。

3.Spring Boot实现版本控制

实现方案:

1.首先创建自定义@APIVersion 注释和自定义URL匹配规则ApiVersionCondition。

2.然后创建自定义 RequestMappingHandlerMapping 匹配对应的request,选择合格的method handler。

1.创建自定义注释

首先,在com.smz.smzuser.config 包下,创建自定义版本号标记 @ApiVersion。

package com.smz.smzuser.config;  import org.springframework.web.bind.annotation.Mapping; import java.lang.annotation.*; /**  * 控制api版本注解  *  * @author dawn Date:2019年4月12日  */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface ApiVersion {   /** @return 版本号 */   int value() default 1; }  

说明: ApiVersion 自定义注释,API版本控制,返回相应的版本号。

2、自定义url匹配逻辑

创建 ApiVersionCondition 类,并继承RequestCondition 接口的功能是:筛选版本号,将提取请求头中的版本号与注释上定义的版本号进行比较,以确定请求应该落在哪里controller上。

在com.smz.smzuser.config 包下创建ApiVersionCondition 类,重写 RequestCondition,创建自定义url匹配逻辑

package com.smz.smzuser.config;  import org.springframework.web.servlet.mvc.condition.RequestCondition;  import javax.servlet.http.HttpServletRequest;  /** * 版本号规则配置类 */ public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {   private int apiVersion;    public ApiVersionCondition(int apiVersion) {     this.apiVersion = apiVersion;   }    @Override   public ApiVersionCondition combine(ApiVersionCondition other) {     // 采用最终定义优先原则,方法上的定义覆盖类上的定义     return new ApiVersionCondition(other.getApiVersion());   }    @Override   public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {     String ver = request.getHeader("version");     // 因为小数来自请求头,所以需要乘以10     int version = Integer.parseInt(ver);     if (version >= this.apiVersion) { // 若要求的版本号大于等于配置版本号, 则满足       return this;     }     return null;   }    @Override   public int compareTo(ApiVersionCondition other, HttpServletRequest request) {     // 最新版本号优先匹配     return other.getApiVersion() - this.apiVersion;   }    public int getApiVersion() {     return apiVersion;   } }   

3.自定义匹配处理器

在com.smz.smzuser.config 包下创建 ApiRequestMappingHandlerMapping 类,重写部分 RequestMappingHandlerMapping 的方法。

package com.smz.smzuser.config;  import org.springframework.core.annotation.AnnotationUtils; import org.springframework.web.servlet.mvc.condition.RequestCondition; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;  import java.lang.reflect.Method;  /** 创建自定义requestMapping类别配置规则 */ public class ApiRequestMappingHandlerMapping extends RequestMappingHandlerMapping {   @Override   protected RequestCondition<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) {     ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);     return createCondition(apiVersion);   }    @Override   protected RequestCondition<ApiVersionCondition> 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());   } }   

4.配置注册自定义RequestMappingHandlerMapping

package com.smz.smzuser.config;  import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; import org.springframework.stereotype.Component; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;  /** 覆盖spring原生RequestMappingHandlerMapping类 */ @Component public class WebMvcRegistrationsConfig implements WebMvcRegistrations {   @Override   public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {     RequestMappingHandlerMapping handlerMapping = new ApiRequestMappingHandlerMapping();     handlerMapping.setOrder(0);     return handlerMapping;   } }  

上面步,把api 版本控制配置完成,都是重写spring boot 内部的处理流程。

4.测试

创建Controller

1、在com.smz.smzuser.web.rest 目录下,分别创建version1 和 version2目录 分别在version1,version2目录下创建UserControllerV1,UserControllerV2

UserControllerV1

package com.smz.smzuser.web.rest.version1;

import com.smz.smzuser.config.ApiVersion;
import com.smz.smzuser.service.UserService;
import com.smz.smzuser.web.transform.UserWebTransform;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RequestMapping("/api")
@ApiVersion(1)
@RestController
public class UserControllerV1 {
  @GetMapping(value = "/user/test")
  public String test() {
    return "version1";
  }
}

UserControllerV2

package com.smz.smzuser.web.rest.version2;

import com.smz.smzuser.config.ApiVersion;
import com.smz.smzuser.service.UserService;
import com.smz.smzuser.web.transform.UserWebTransform;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RequestMapping("/api")
@ApiVersion(2)
@RestController
public class UserControllerV2 {
  @GetMapping(value = "/user/test")
  public String test() {
    return "user v2 test";
  }
}

2、启动项目后,输入相关地址,查看版本控制是否生效 正常版本地址

image.png

更高版本地址: 说明:有一种实现方式是直接在url地址中写明版本号例如:/api/v1/user /api/v2/user 虽然依然可以实现版本控制但是如果使用更高的APP版本就会出现不可以兼容问题 例如接口是v2,app版本是v3这样就不可以访问了相当于接口被写死了 这种方式就可以解决向下兼容问题,就是说可以继承没有迭代版本的接口

遇到的问题

1.因为用到Swagger2所以使用了相关配置导致配置文件继承了WebMvcConfigurationSupport会到导致我们配置的自定义配置失效 在启动时会报错:说是方法1已经映射了路径,就不能再使用了,由于两个requestMapping 里面url被识别 一样 解决办法: 1.删掉继承WebMvcConfigurationSupport的配置文件 如果启动报错

Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException

那么试一下降低SpringBoot版本2.6.7->2.5.7 启动后运行成功

完结

标签: smz射频同轴连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台