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

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

Restful API設計規範裡面提到:接口需要帶版本號信息。一般接口上線以後,避免不了需求的變更、接口的變更。可能是接口參數發生變更,也有可能是業務邏輯需要調整,如果直接在原來的接口上進行修改,必然會影響原有服務的正常運行。

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

  • 自定義版本號標記註解
@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<apiversioncondition> {
/**
* 接口路徑中的版本號前綴,如: 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();
}
}
/<apiversioncondition>
  • 重寫部分 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<apiversioncondition> 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);
}
}
/<apiversioncondition>
  • 配置註冊自定義的 CustomRequestMappingHandlerMapping
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
public RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new CustomRequestMappingHandlerMapping();
}
}


分享到:


相關文章: