原文:https://blog.mariojd.cn/how-to-design-spring-boot-api-version-number-elegantly.html

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

   常见的解决方案,是在接口路径中加入版本号用于区分,此外还可以在参数甚至 header 里带上版本号。这里以在请求路径中带上版本号为例,如:http://IP:PORT/api/v1/test ,v1 即代表的是版本号。当然了,可以像这样,直接写死在 @RequestMapping("api/v1/test") 属性中,不过下面提供了更为优雅的解决方案。

1. 自定义版本号标记注解:

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

2. 重写相应的 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;
} /**
* 最近优先原则,方法定义的 @ApiVersion > 类定义的 @ApiVersion
*/
@Override
public ApiVersionCondition combine(ApiVersionCondition other) {
return new ApiVersionCondition(other.getApiVersion());
} /**
* 获得符合匹配条件的ApiVersionCondition
*/
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
if (m.find()) {
int version = Integer.valueOf(m.group(1));
if (version >= getApiVersion()) {
return this;
}
}
return null;
} /**
* 当出现多个符合匹配条件的ApiVersionCondition,优先匹配版本号较大的
*/
@Override
public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
return other.getApiVersion() - getApiVersion();
} }

3. 重写部分 RequestMappingHandlerMapping 的方法

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

4. 配置注册自定义的 CustomRequestMappingHandlerMapping

@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport { @Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new CustomRequestMappingHandlerMapping();
} }

5. 编写接口,标记上相应的 @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;
} }

6. 启动 Application,测试及查看结果

如何优雅的设计 Spring Boot API 接口版本号的更多相关文章

  1. Spring Boot入门系列(二十一)如何优雅的设计 Restful API 接口版本号,实现 API 版本控制!

    前面介绍了Spring Boot 如何快速实现Restful api 接口,并以人员信息为例,设计了一套操作人员信息的接口.不清楚的可以看之前的文章:https://www.cnblogs.com/z ...

  2. spring boot rest 接口集成 spring security(2) - JWT配置

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  3. Spring Boot API 统一返回格式封装

    今天给大家带来的是Spring Boot API 统一返回格式封装,我们在做项目的时候API 接口返回是需要统一格式的,只有这样前端的同学才可对接口返回的数据做统一处理,也可以使前后端分离 模式的开发 ...

  4. 使用JWT设计SpringBoot项目api接口安全服务

    转载直: 使用JWT设计SpringBoot项目api接口安全服务

  5. spring boot rest 接口集成 spring security(1) - 最简配置

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  6. Swagger2 生成 Spring Boot API 文档

    Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.本文主要介绍了在 Spring Boot 添加 Swagger 支持, 生成可自动维护的 A ...

  7. 如何优雅地停止 Spring Boot 应用?

    首先来介绍下什么是优雅地停止,简而言之,就是对应用进程发送停止指令之后,能保证正在执行的业务操作不受影响,可以继续完成已有请求的处理,但是停止接受新请求. 在 Spring Boot 2.3 中增加了 ...

  8. spring boot:给接口增加签名验证(spring boot 2.3.1)

    一,为什么要给接口做签名验证? 1,app客户端在与服务端通信时,通常都是以接口的形式实现, 这种形式的安全方面有可能出现以下问题: 被非法访问(例如:发短信的接口通常会被利用来垃圾短信) 被重复访问 ...

  9. 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

随机推荐

  1. unity 自实现协程总结

    unity本人自实现了一个协程调用. 只是moveNext()的简单协程调用和封装,这个没什么好说的, 网上例子一大堆. 但使用的过程中遇到了几个问题. 1. 自己写的moveNext() 协程不能等 ...

  2. vs遇到的字符串问题

    原以为自己的字符串已经理解不错了, 今天又被vs搞了. 情景就不说了, 直接说结果: 有两种情况 1 当文件是存储为gbk或者utf-8的时候, 中文字符存储永远是gbk的值. ‘按’字的gbk编码 ...

  3. java 面向对象(三):类结构 属性

    类的设计中,两个重要结构之一:属性 对比:属性 vs 局部变量 1.相同点: * 1.1 定义变量的格式:数据类型 变量名 = 变量值 * 1.2 先声明,后使用 * 1.3 变量都其对应的作用域 2 ...

  4. Newbe.Claptrap 框架中为什么用 Claptrap 和 Minion 两个词?

    Newbe.Claptrap 框架中为什么用 Claptrap 和 Minion 两个词?最近整理了一下项目的术语表.今天就谈谈为什么起了 Claptrap 和 Minion 两个名字. Claptr ...

  5. [USACO3.1]形成的区域(扫描线+离散化)

    [USACO3.1]形成的区域(P6432) 日期:2020-05-31 目录 [USACO3.1]形成的区域(P6432) 一.题意分析 二.算法分析 1. 暴力 0). 初始状态(红点为原点) 1 ...

  6. day4:运算符

    1.算术运算符:+ - * / // % ** 注意点:1./ 除法,结果为小数  2.// 地板除,返回整数  3.如果被除数或者除数是一个小数,结果加上.0 2.比较运算符:< > & ...

  7. bzoj3442学习小组

    bzoj3442学习小组 题意: 共有n个学生,m个学习小组,每个学生只愿意参加其中的一些学习小组,且一个学生最多参加k个学习小组.每个学生参加学习小组财务处都收一定的手续费,不同的学习小组有不同的手 ...

  8. Facebook没有测试工程师,如何进行质量控制的?

    Facebook从04年的哈佛校园的学生项目在短短的7-8年的时间中快速增长为拥有10亿用户的世界上最大的社交网络,又一次见证了互联网创业成功的奇迹.同时它的产品研发流程也成为了众多互联网产品公司的追 ...

  9. C++语法小记---类型转换

    类型转换 C++类型转换主要包含两种: 隐式类型转换 小类型向大类型转换 char -> int short ->int int -> unsigned int int -> ...

  10. abp vnext 开发快速入门 4 跨域设置

    由于项目采用的微服务框架,前端与后端交互难免有跨域的问题.abp vnext实现跨域也很简单,只需要设置几处就可以了,这里只讲全局的跨域,至于局部的Action如何跨域请自行搜索.netcore 跨域 ...