spring boot 接口版本控制

对于应用上线后的接口变动,前后端代码更新无法做到同时更新,容易造成报错,影响用户体验。需做好接口版本控制,在前端代码更迭时期保留旧版本接口的服务提供。

ApiVersion.java

package baseUtils;

import org.springframework.web.bind.annotation.Mapping;

import java.lang.annotation.*;

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

     /**
     * api version begin 1
     */
    double version() default 1;
}

ApiVersionCondition.java

package baseUtils;

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

import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ApiVersionCondition implements RequestCondition {

    /**
     * 接口路径中的版本号前缀,如: api/v[1-n]/test
     */
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v([0-9]+\\.{0,1}[0-9]{0,2})/");

    /** API VERSION interface **/
    private ApiVersion apiVersion;

    ApiVersionCondition(ApiVersion apiVersion){
        this.apiVersion = apiVersion;
    }

    /**
     * [当class 和 method 请求url相同时,触发此方法用于合并url]
     * 官方解释:
     * - 某个接口有多个规则时,进行合并
     * - 比如类上指定了@RequestMapping的 url 为 root
     * - 而方法上指定的@RequestMapping的 url 为 method
     * - 那么在获取这个接口的 url 匹配规则时,类上扫描一次,方法上扫描一次,这个时候就需要把这两个合并成一个,表示这个接口匹配root/method
     * @param other 相同api version condition
     * @return ApiVersionCondition
     */
    @Override
    public ApiVersionCondition combine(ApiVersionCondition other) {
        // 此处按优先级,method大于class
        return new ApiVersionCondition(other.getApiVersion());
    }

    /**
     * 判断是否成功,失败返回 null;否则,则返回匹配成功的条件
     * @param httpServletRequest http request
     * @return 匹配成功条件
     */
    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {
        // 通过uri匹配版本号
        System.out.println(httpServletRequest.getRequestURI());
        Matcher m = VERSION_PREFIX_PATTERN.matcher(httpServletRequest.getRequestURI());
        if (m.find()) {
            // 获得符合匹配条件的ApiVersionCondition
            System.out.println("groupCount:"+m.groupCount());
            double version = Double.valueOf(m.group(1));
            if (version >= getApiVersion().version()) {
                return this;
            }
        }
        return null;
    }

    /**
     * 多个都满足条件时,用来指定具体选择哪一个
     * @param other 多个时
     * @param httpServletRequest http request
     * @return 取版本号最大的
     */
    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest httpServletRequest) {
        // 当出现多个符合匹配条件的ApiVersionCondition,优先匹配版本号较大的
        return other.getApiVersion().version() >= getApiVersion().version() ? 1 : -1;
    }

    public ApiVersion getApiVersion() {
        return apiVersion;
    }
}

ApiVersionRequestMappingHandlerMapping.java

package baseUtils;

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;

public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    /**
     * class condition
     * - 在class上加@ApiVersion注解&url加{version}
     * @param handlerType class type
     * @return ApiVersionCondition
     */
    @Override
    protected RequestCondition getCustomTypeCondition(Class handlerType) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType,ApiVersion.class);
        return null == apiVersion ? super.getCustomTypeCondition(handlerType) : new ApiVersionCondition(apiVersion);
    }

    /**
     * method condition
     * - 在方法上加@ApiVersion注解&url加{version}
     * @param method method object
     * @return ApiVersionCondition
     */
    @Override
    protected RequestCondition getCustomMethodCondition(Method method) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(method,ApiVersion.class);
        return null == apiVersion ? super.getCustomMethodCondition(method) : new ApiVersionCondition(apiVersion);
    }
}

ApiConfiguration.java

package baseUtils;


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 ApiConfiguration implements WebMvcRegistrations {
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        return new ApiVersionRequestMappingHandlerMapping();
    }
}

测试接口

package controller;


import baseUtils.ApiVersion;
import baseUtils.ResultVO;
import baseUtils.ResultVOUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
@ResponseBody
public class CenterController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 查询所有用户信息
     */
    @ApiVersion
    @RequestMapping(value = "{version}/users/{guid}", method = RequestMethod.GET)
    @ResponseBody
    public ResultVO getUsers(@PathVariable("guid") String GUID){
        return ResultVOUtil.success("guid="+GUID+",1.0版本");
    }

    /**
     * 查询所有用户信息
     */
    @ApiVersion(version = 1.1)
    @RequestMapping(value = "{version}/users/{guid}", method = RequestMethod.GET)
    @ResponseBody
    public ResultVO getUsersV2(@PathVariable("guid") String GUID){
        return ResultVOUtil.success("guid="+GUID+",1.1版本");
    }


}

感谢原文作者,点击查看原文

你可能感兴趣的:(Java,spring,boot,java)