springboot api版本管理 修改API映射的方式

1 应用场景

1、接口版本迭代过程中

/rest/market/general      第一版:提供基础数据          用户:我不想升级
/rest/market/general      第二版:提供基础数据+补充数据  用户:升级程序

2、对外提供接口

服务中只提供一个url /rest/market/general
根据不同的约定方式去取数据
比如: 在header中携带不同的参数访问不同的接口

2 解决方法

  • 保留原有的url映射绑定
  • 增加新的绑定方式
  • 修改接口映射的条件

3、自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiSign {

   /**
    * 这几个参数不是关键,根据自己条件进行自定义
    * @return
    */
   String value();

   String headerKey() default "headerKey";

   String paramKey() default "paramKey";
}

4、定制每次请求的匹配条件

import org.springframework.beans.BeanUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;

import javax.servlet.http.HttpServletRequest;

/**
 * api请求条件判断
 */
public class APIRequestCondition implements RequestCondition<APIRequestCondition> {
   private String paramKey;

   private String headerKey;

   private String apiName;

   public APIRequestCondition() {
   }

   public APIRequestCondition(String paramKey, String headerKey, String apiName) {
      this.paramKey = paramKey;
      this.headerKey = headerKey;
      this.apiName = apiName;
   }

   /**
    * 将类上的注解和方法上的注解进行合并
    *
    * @param other
    * @return
    */
   @Override
   public APIRequestCondition combine(APIRequestCondition other) {
      APIRequestCondition requestCondition = new APIRequestCondition();
      BeanUtils.copyProperties(other, requestCondition);
      return requestCondition;
   }
   
   /**
    * 请求过程的匹配规则
    *
    * @param request
    * @return
    */
   @Override
   public APIRequestCondition getMatchingCondition(HttpServletRequest request) {
      String apiHeader = request.getHeader(headerKey);
      if(apiName.equals(apiHeader)) {
         return this;
      }
      return null;
   }

   @Override
   public int compareTo(APIRequestCondition other, HttpServletRequest request) {
      return 0;
   }
   
   /**
    * 校验请求头格式
    *
    * @param request
    * @return
    */
   private String checkRequestHeader(HttpServletRequest request) {
      return request.getHeader(headerKey);
   }
}

5、增加自定义的接口匹配规则

import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;

/**
 * 请求映射,把方法关联到自定义的apiRequestCondition
 */
public class ApiHandlerMapping extends RequestMappingHandlerMapping {

   /**
    * 锁定扫描条件
    * @param beanType
    * @return
    */
   @Override
   protected boolean isHandler(Class<?> beanType) {
      return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
         AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
   }

   /**
    * 管理定制的在类上访问条件
    * @param handlerType
    * @return
    */
   @Override
   protected RequestCondition<APIRequestCondition> getCustomTypeCondition(Class<?> handlerType) {
      ApiSign annotation = AnnotationUtils.findAnnotation(handlerType, ApiSign.class);
      return createCondition(annotation);
   }

   /**
    * 管理自己在方法上的访问条件
    * @param method
    * @return
    */
   @Override
   protected RequestCondition<APIRequestCondition> getCustomMethodCondition(Method method) {
      ApiSign apiName = AnnotationUtils.findAnnotation(method, ApiSign.class);
      return createCondition(apiName);
   }

   /**
    * 初始化自定义的访问条件
    * @param apiName
    * @return
    */
   private RequestCondition<APIRequestCondition> createCondition(ApiSign apiName) {
      return apiName == null ? null : new APIRequestCondition(apiName.paramKey(), apiName.headerKey(), apiName.value());
   }
}

6、容器加载过程注入接口匹配规则

import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@Configuration
public class RequestMappingConfig
   implements WebMvcRegistrations
{
   @Override
   public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
      return new ApiHandlerMapping();
   }
}

7、 测试

import com.test.demo.Api.ApiSign;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/rest/market/general")
public class TestController {

   @GetMapping("/base")
   @ApiSign("test")
   public String getData(){
      return "不想升级:基础数据";
   }

   @GetMapping("/base")
   @ApiSign("vip")
   public String getNewData(){
      return "升级:基础数据 + 补充数据";
   }

   @GetMapping("/base")
   public String getNullData(){
      return "can not see";
   }
   
}

测试结果:

接口header带 test :显示 不想升级:基础数据

接口header带 vip:升级:基础数据 + 补充数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值