SpingMVC框架實現restfull介面的版本控制
阿新 • • 發佈:2019-02-07
網際網路發展到今天,基於restfull開發的系統也越來越多,不再侷限於jsp等指令碼語言來實現動態資料的展示,而是通過後臺提供的http介面給前端呼叫,但是當系統越做越大,同一個介面可能會不斷的修改,一旦呼叫方式發生改變,後果是非常嚴重的,客戶端將無法正常呼叫,除非強制客戶端升級到最新版本,這個也是不太現實的,那怎麼辦呢?我們想到的就是通過版本來控制同一個介面,類似這樣的一個地址:http://localhost:8080/api/v1,其中v1就是當前所用到的版本,那怎麼來實現呢?直接上程式碼:
首先自定義一個註解:
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 @Mapping public @interface ApiVersion { /** * 標識版本號 * @return */ int value(); }
這個註解標識當前介面所對應的版本
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.sunsharp.platform.annotation.ApiVersion; import cn.sunsharp.platform.web.BaseController; @RequestMapping("{version}") @RestController public class HelloController extends BaseController { @ApiVersion(1) @RequestMapping("hello") public String hello1(){ return "hello1"; } @ApiVersion(2) @RequestMapping("hello") public String hello2(){ return "hello2"; } }
這樣我們通過v1,v2這樣的地址就能訪問到對應的版本,但是這樣會有一個問題,如果客戶端傳入v3,但是所定義的版本並沒有定義v3,此時客戶端肯定會報404錯誤,也就是沒有這個頁面,怎麼辦呢,相當的就是攔截器,如果傳入的版本沒有,則預設訪問最新的版本,請看程式碼:
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> { // 路徑中版本的字首, 這裡用 /v[1-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) { // 採用最後定義優先原則,則方法上的定義覆蓋類上面的定義 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) // 如果請求的版本號大於配置版本號, 則滿足 return this; } return null; } public int compareTo(ApiVersionCondition other, HttpServletRequest request) { // 優先匹配最新的版本號 return other.getApiVersion() - this.apiVersion; } public int getApiVersion() { return apiVersion; } }
import java.lang.reflect.Method;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import cn.sunsharp.platform.annotation.ApiVersion;
public class CustomRequestMappingHandlerMapping 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());
}
}
這樣,當傳入的版本沒有則預設會訪問最新的版本,還沒有完,還需要註冊這個攔截器:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import cn.sunsharp.platform.version.CustomRequestMappingHandlerMapping;
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Override
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
return handlerMapping;
}
}
如此就實現了最基本的對api的版本控制。