• springboot实现WebAPI版本控制


    Springboot实现webAPI版本控制

    1.定义一个自定义版本注解

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ApiVersion {
        /**
         * @return版本号
         */
        int value( ) default 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.自定义URL匹配规则ApiVersionCondition

    package com.yangjunbo.helloword.properties;
    
    import org.springframework.web.servlet.mvc.condition.RequestCondition;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /*接下来定义URL匹配逻辑,创建ApiVersionCondition类并继承RequestCondition接口,
            其作用是进行版本号筛选,将提取请求URL中的版本号与注解上定义的版本号进行对比,以此来判断某个请求应落在哪个控制器上。*/
    public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
        private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile(".*v(\\d+).*");
    
        private int apiVersion;
        ApiVersionCondition(int apiVersion) {
            this.apiVersion = apiVersion;
        }
        private int getApiVersion() {
            return apiVersion;
        }
    
        @Override
        public ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {
            return new ApiVersionCondition(apiVersionCondition.getApiVersion());
        }
        @Override
        public ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {
            Matcher m = VERSION_PREFIX_PATTERN.matcher(httpServletRequest.getRequestURI());
            if (m.find()) {
                Integer version = Integer.valueOf(m.group(1));
                if (version >= this.apiVersion) {
                    //apiVersion = version;
                    return this;
                }
            }
            return null;
        }
        @Override
        public int compareTo(ApiVersionCondition apiVersionCondition, HttpServletRequest httpServletRequest) {
            return apiVersionCondition.getApiVersion() - this.apiVersion;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    3.使用RequestMappingHandlerMapping创建自定义的映射处理程序,根据Request参数匹配符合条件的处理程序

    package com.yangjunbo.helloword.properties;
    
    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;
    
    public class ApiRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
        private static final String VERSION_FLAG = "{version}";
    
        private static RequestCondition<ApiVersionCondition> createCondition(Class<?> clazz) {
            RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);
            if (classRequestMapping == null) {
                return null;
            }
            StringBuilder mappingUrlBuilder = new StringBuilder();
            if (classRequestMapping.value().length > 0) {
                mappingUrlBuilder.append(classRequestMapping.value()[0]);
            }
            String mappingUrl = mappingUrlBuilder.toString();
            if (!mappingUrl.contains(VERSION_FLAG)) {
                return null;
            }
            ApiVersion apiVersion = clazz.getAnnotation(ApiVersion.class);
            return apiVersion == null ? new ApiVersionCondition(1) : new ApiVersionCondition(apiVersion.value());
        }
        @Override
        protected RequestCondition<?> getCustomMethodCondition(Method method) {
            return createCondition(method.getClass());
        }
        @Override
        protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
            return createCondition(handlerType);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    4.配置注册自定义的RequestMappingHandlerMapping

    package com.yangjunbo.helloword.properties;
    
    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 WebMvcRegistrationsConfig implements WebMvcRegistrations {
        @Override
        public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
            return new ApiRequestMappingHandlerMapping();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5.创建测试接口

    package com.yangjunbo.helloword.controller.v1;
    
    import com.yangjunbo.helloword.common.JSONResult;
    import com.yangjunbo.helloword.properties.ApiVersion;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    // V1 版本的接口定义
    @RestController
    @RequestMapping("api/{version}/order")
    public class OrderV1Controller {
        @GetMapping("/delete/{orderId}")
        public JSONResult deleteOrderById(@PathVariable String orderId) {
            System.out.println("V1删除订单成功:"+orderId);
            return JSONResult.ok("V1删除订单成功");
        }
    
        @GetMapping("/detail/{orderId}")
        public JSONResult queryOrderById(@PathVariable String orderId) {
            System.out.println("V1获取订单详情成功:"+orderId);
            return JSONResult.ok("V1获取订单详情成功");
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    package com.yangjunbo.helloword.controller.v2;
    
    import com.yangjunbo.helloword.common.JSONResult;
    import com.yangjunbo.helloword.properties.ApiVersion;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    // V2 版本的接口定义
    @ApiVersion(2)
    @RestController
    @RequestMapping("api/{version}/order")
    public class OrderV2Controller {
        @GetMapping("/detail/{orderId}")
        public JSONResult queryOrdearById(@PathVariable String orderId) {
            System.out.println("V2获取订单详情成功:"+orderId);
            return JSONResult.ok("V2获取订单详情成功");
        }
    
        @GetMapping("/list")
        public JSONResult list() {
            System.out.println("V2,新增list订单列表接口");
            return JSONResult.ok(200,"V2,新增list订单列表接口");
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    6.访问接口测试

    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    以上验证情况说明Web API的版本控制配置成功,实现了旧版本的稳定和新版本的更新。
    1)当请求正确的版本地址时,会自动匹配版本的对应接口。
    2)当请求的版本大于当前版本时,默认匹配最新的版本。
    3)高版本会默认继承低版本的所有接口。实现版本升级只关注变化的部分,没有变化的部分会自动平滑升级,这就是所谓的版本继承。
    4)高版本的接口的新增和修改不会影响低版本。
    这些特性使得在升级接口时,原有接口不受影响,只关注变化的部分,没有变化的部分自动平滑升级。这样使得Web API更加简洁,这就是实现Web API版本控制的意义所在。
    参考书籍 《springboot从入门到实战-章为忠著》

  • 相关阅读:
    【深入设计模式】模板方法模式—让你更科学地复用代码
    背包问题求最优解方案数
    记一次 .NET 某游戏网站 CPU爆高分析
    大数据-玩转数据-Flink SQL编程
    1分钟了解C语言正确使用字节对齐及#pragma pack的方法
    jdbc——行文架构
    Java项目:ssm实验室预约维修管理系统
    Webpack面试题
    项目资讯丨轻空间中标连云港市首座“多功能声学综合馆”(EPC)
    make&Makefile
  • 原文地址:https://blog.csdn.net/Rockandrollman/article/details/127821375