URLhtml
http://example.com/v1/helloworldjava
HEADERweb
http://www.lexicalscope.com/blog/2012/03/12/how-are-rest-apis-versioned/spring
原理 在SpringMVC中RequestMappingHandlerMapping是比較重要的一個角色,它決定了每一個URL分發至哪一個Controller。api
Spring Boot加載過程以下,因此咱們能夠經過自定義WebMvcRegistrationsAdapter來改寫RequestMappingHandlerMapping。bash
ApiVersion.javarestful
package com.freud.apiversioning.configuration;
import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;
import org.springframework.web.bind.annotation.Mapping;
@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documented@Mappingpublic @interface ApiVersion {
/** * version * * @return */ int value();}複製代碼
ApiVersionCondition.javamvc
package com.freud.apiversioning.configuration;
import java.util.regex.Matcher;import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
// extract the version part from url. example [v0-9] private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");
private int apiVersion;
public ApiVersionCondition(int apiVersion) { this.apiVersion = apiVersion; }
public ApiVersionCondition combine(ApiVersionCondition other) { // latest defined would be take effect, that means, methods definition with // override the classes definition return new ApiVersionCondition(other.getApiVersion()); }
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) { Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI()); if (m.find()) { Integer version = Integer.valueOf(m.group(1)); if (version >= this.apiVersion) // when applying version number bigger than configuration, then it will take // effect return this; } return null; }
public int compareTo(ApiVersionCondition other, HttpServletRequest request) { // when more than one configured version number passed the match rule, then only // the biggest one will take effect. return other.getApiVersion() - this.apiVersion; }
public int getApiVersion() { return apiVersion; }
}複製代碼
ApiVersioningRequestMappingHandlerMapping.javaapp
package com.freud.apiversioning.configuration;
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 ApiVersioningRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override protected RequestCondition<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) { ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class); return createCondition(apiVersion); }
@Override protected RequestCondition<ApiVersionCondition> 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()); }}複製代碼
WebMvcRegistrationsConfig.javaide
package com.freud.apiversioning.configuration;
import org.springframework.boot.autoconfigure.web.WebMvcRegistrationsAdapter;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@Configurationpublic class WebMvcRegistrationsConfig extends WebMvcRegistrationsAdapter {
@Override public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { return new ApiVersioningRequestMappingHandlerMapping(); }
}複製代碼
TestVersioningController.java
package com.freud.apiversioning.v1.controller;
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
import com.freud.apiversioning.configuration.ApiVersion;
@ApiVersion(1)@RequestMapping("/{api_version}")@RestController("TestVersioningController-v1")public class TestVersioningController {
@RequestMapping("/hello") public String hello() { return "hello v1"; }}複製代碼
TestVersioningController.java
package com.freud.apiversioning.v2.controller;
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
import com.freud.apiversioning.configuration.ApiVersion;
@ApiVersion(2)@RequestMapping("/{api_version}")@RestController("TestVersioningController-v2")public class TestVersioningController {
@RequestMapping("/hello") public String hello() { return "hello v2"; }}複製代碼
ApiVersioningApplication.java
package com.freud.apiversioning;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class ApiVersioningApplication {
public static void main(String[] args) { SpringApplication.run(ApiVersioningApplication.class, args); }}複製代碼
application.yml
server: port: 7905複製代碼
v1 訪問http://localhost:7905/v1/hello
v2 訪問http://localhost:7905/v2/hello
v100 訪問http://localhost:7905/v100/hello
Spring Boot API 版本權限控制: http://blog.csdn.net/u010782227/article/details/74905404
讓SpringMVC支持可版本管理的Restful接口:
http://www.cnblogs.com/jcli/p/springmvcrestfulversion.html
如何作到API兼容:
https://kb.cnblogs.com/page/108253/
解析@EnableWebMvc 、WebMvcConfigurationSupport和WebMvcConfigurationAdapter:
http://blog.csdn.net/pinebud55/article/details/53420481
How are REST APIs versioned?:
http://blog.csdn.net/pinebud55/article/details/53420481