查看原文
其他

老弟,你这么设计 API 接口版本号,为接手的同事考虑过吗?

来源:http://i7q.cn/4vGjSN


一般来说,系统上线以后,需求仍会发生变动,功能也会迭代更新。可能是接口参数发生变更,也有可能是业务逻辑需要调整,如果直接在原来的接口上进行修改,必然会影响原有服务的正常运行。


常见的解决方案,是在接口路径中加入版本号用于区分,此外还可以在参数甚至 header 里带上版本号。这里以在请求路径中带上版本号为例,


如:http://IP:PORT/api/v1/test ,v1 即代表的是版本号。当然了,可以像这样,直接写死在 @RequestMapping("api/v1/test") 属性中,不过下面提供了更为优雅的解决方案。


# 自定义版本号标记注解

@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ApiVersion {
/** * 标识版本号,从1开始 */ int value() default 1;
}

# 重写相应的 RequestCondition


@Data@Slf4jpublic 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(); }
}
# 重写部分 RequestMappingHandlerMapping 的方法

@Slf4jpublic 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); }
}

# 配置注册自定义的 CustomRequestMappingHandlerMapping


@Slf4j@Configurationpublic 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,测试及查看结果




热文推荐

Java面试题汇总(15)

如果你被裁员,赔偿金是N、N+1、2N呢?

今天,咱们来讨论一下,一份工作坚持多久跳槽最合适?




觉得不错,请给个「在看」

分享给你的朋友!


- End-

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存