在spring boot项目中,如果要进行restful接口的版本控制一般有以下几个方向:
- 基于path的版本控制
- 基于header的版本控制
在spring MVC下,url映射到哪个method是由RequestMappingHandlerMapping来控制的,那么我们也是通过 RequestMappingHandlerMapping来做版本控制的
1.path控制
首先写一个annocation
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiVersion {
String value();
}
ApiVersionCondition 用来控制当前request 指向哪个method
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
private static final Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/(v\\d+)/");
private final String version;
public ApiVersionCondition(String version) {
this.version = version;
}
@Override
public ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {
return null;
}
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {
Matcher m = VERSION_PREFIX_PATTERN.matcher(httpServletRequest.getRequestURI());
if (m.find()) {
String pathVersion = m.group(1);
if (Objects.equals(pathVersion, version)) {
return this;
}
}
return null;
}
@Override
public int compareTo(ApiVersionCondition apiVersionCondition, HttpServletRequest httpServletRequest) {
return 0;
}
}
PathVersionHandlerMapping
public class PathVersionHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
}
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType,ApiVersion.class);
return createCondition(apiVersion);
}
@Override
protected RequestCondition<?> 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());
}
}
WebMvcConfiguration
@Configuration
public class WebMvcConfiguration implements WebMvcRegistrations {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new PathVersionHandlerMapping();
}
}
Controller
@RestController
@RequestMapping(value = "/{version}/article")
public class ArticleController {
@GetMapping(value = "one")
public String query(){
return "article api default";
}
@GetMapping(value = "one")
@ApiVersion("v1")
public String query2(){
return "article api v1";
}
}
根据代码逻辑如果version是v1 那么就会匹配到v1的方法,否则则会匹配到默认方法。
达到预期。
2.header控制
annocation
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiVersion {
String value();
}
ApiVersionCondition
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
private static final String X_VERSION = "X-VERSION";
private final String version ;
public ApiVersionCondition(String version) {
this.version = version;
}
@Override
public ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {
return null;
}
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {
String headerVersion = httpServletRequest.getHeader(X_VERSION);
if(Objects.equals(version,headerVersion)){
return this;
}
return null;
}
@Override
public int compareTo(ApiVersionCondition apiVersionCondition, HttpServletRequest httpServletRequest) {
return 0;
}
}
HeaderVersionRequestMappingHandlerMapping
public class HeaderVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType,ApiVersion.class);
return createCondition(apiVersion);
}
@Override
protected RequestCondition<?> 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());
}
}
WebMvcConfiguration
@Configuration
public class WebMvcConfiguration implements WebMvcRegistrations {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new HeaderVersionRequestMappingHandlerMapping();
}
}
Controller
@RestController
@RequestMapping(value = "question")
public class QuestionController {
@GetMapping(value = "one")
public String query(){
return "default";
}
@GetMapping(value = "one")
@ApiVersion("v1")
public String query2(){
return "v1";
}
}
效果截图
达到预期。
至于用哪种模式,根据自己情况选择就可以。