如何優雅的設計 Spring Boot API 接口版本號

一般來說,系統上線以後,需求仍會發生變動,功能也會迭代更新。可能是接口參數發生變更,也有可能是業務邏輯需要調整,如果直接在原來的接口上進行修改,必然會影響原有服務的正常運行。

常見的解決方案,是在接口路徑中加入版本號用於區分,此外還可以在參數甚至 header 裡帶上版本號。這裡以在請求路徑中帶上版本號為例,如:
http://IP:PORT/api/v1/test ,v1 即代表的是版本號。當然了,可以像這樣,直接寫死在 @RequestMapping("api/v1/test") 屬性中,不過下面提供了更為優雅的解決方案。

  • 自定義版本號標記註解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {
 /**
 * 標識版本號,從1開始
 */
 int value() default 1;
}
  • 重寫相應的 RequestCondition
@Data
@Slf4j
public class ApiVersionCondition implements RequestCondition {
 /**
 * 接口路徑中的版本號前綴,如: api/v[1-n]/test
 */
 private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v(\\d+)/");
 private int apiVersion;
 ApiVersionCondition(int apiVersion) {
 this.apiVersion = apiVersion;
 }
 @Override
 public ApiVersionCondition combine(ApiVersionCondition other) {
 // 最近優先原則,方法定義的 @ApiVersion > 類定義的 @ApiVersion
 return new ApiVersionCondition(other.getApiVersion());
 }
 @Override
 public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
 Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
 if (m.find()) {
 // 獲得符合匹配條件的ApiVersionCondition
 int version = Integer.valueOf(m.group(1));
 if (version >= getApiVersion()) {
 return this;
 }
 }
 return null;
 }
 @Override
 public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
 // 當出現多個符合匹配條件的ApiVersionCondition,優先匹配版本號較大的
 return other.getApiVersion() - getApiVersion();
 }
}
  • 重寫部分 RequestMappingHandlerMapping 的方法
@Slf4j
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
 @Override
 protected RequestCondition> getCustomTypeCondition(Class> handlerType) {
 // 掃描類上的 @ApiVersion
 ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
 return createRequestCondition(apiVersion);
 }
 @Override
 protected RequestCondition> getCustomMethodCondition(Method method) {
 // 掃描方法上的 @ApiVersion
 ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
 return createRequestCondition(apiVersion);
 }
 private RequestCondition createRequestCondition(ApiVersion apiVersion) {
 if (Objects.isNull(apiVersion)) {
 return null;
 }
 int value = apiVersion.value();
 Assert.isTrue(value >= 1, "Api Version Must be greater than or equal to 1");
 return new ApiVersionCondition(value);
 }
}
  • 配置註冊自定義的 CustomRequestMappingHandlerMapping
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
 @Override
 public RequestMappingHandlerMapping requestMappingHandlerMapping() {
 return new CustomRequestMappingHandlerMapping();
 }
}
  • 編寫接口,標記上相應的 @ApiVersion
@Slf4j
@ApiVersion
@RestController
@RequestMapping("api/{version}/test")
public class TestController {
 @GetMapping
 public String test01(@PathVariable String version) {
 return "test01 : " + version;
 }
 @GetMapping
 @ApiVersion(2)
 public String test02(@PathVariable String version) {
 return "test02 : " + version;
 }
}
  • 啟動 Application,測試及查看結果
如何優雅的設計 Spring Boot API 接口版本號

最後針對於java程序員價值上萬架構視頻資料免費分享給大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分佈式、高併發等架構技術資料),希望能幫助到且找到一個好的工作,也節省大家在網上搜索資料的時間來學習,也可以關注我一下以後會有更多幹貨分享。要資料請私信回覆:獲取免費資料!!


分享到:


相關文章: