对于应用上线后的接口变动,前后端代码更新无法做到同时更新,容易造成报错,影响用户体验。需做好接口版本控制,在前端代码更迭时期保留旧版本接口的服务提供。
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<ApiVersionCondition> {
/**
* 接口路径中的版本号前缀,如: 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版本");
}
}
感谢原文作者,点击查看原文