如何优雅的设计 Spring Boot API 接口版本号
原文: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 接口版本号的更多相关文章
- Spring Boot入门系列(二十一)如何优雅的设计 Restful API 接口版本号,实现 API 版本控制!
前面介绍了Spring Boot 如何快速实现Restful api 接口,并以人员信息为例,设计了一套操作人员信息的接口.不清楚的可以看之前的文章:https://www.cnblogs.com/z ...
- spring boot rest 接口集成 spring security(2) - JWT配置
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- Spring Boot API 统一返回格式封装
今天给大家带来的是Spring Boot API 统一返回格式封装,我们在做项目的时候API 接口返回是需要统一格式的,只有这样前端的同学才可对接口返回的数据做统一处理,也可以使前后端分离 模式的开发 ...
- 使用JWT设计SpringBoot项目api接口安全服务
转载直: 使用JWT设计SpringBoot项目api接口安全服务
- spring boot rest 接口集成 spring security(1) - 最简配置
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- Swagger2 生成 Spring Boot API 文档
Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.本文主要介绍了在 Spring Boot 添加 Swagger 支持, 生成可自动维护的 A ...
- 如何优雅地停止 Spring Boot 应用?
首先来介绍下什么是优雅地停止,简而言之,就是对应用进程发送停止指令之后,能保证正在执行的业务操作不受影响,可以继续完成已有请求的处理,但是停止接受新请求. 在 Spring Boot 2.3 中增加了 ...
- spring boot:给接口增加签名验证(spring boot 2.3.1)
一,为什么要给接口做签名验证? 1,app客户端在与服务端通信时,通常都是以接口的形式实现, 这种形式的安全方面有可能出现以下问题: 被非法访问(例如:发短信的接口通常会被利用来垃圾短信) 被重复访问 ...
- 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版
欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...
随机推荐
- Git篇---将秘钥添加到github
第1步:创建SSH Key.在用户主目录下,看看有没有-/.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步.如果没有,打开She ...
- day56 js收尾,jQuery前戏
目录 一.原生js事件绑定 1 开关灯案例 2 input框获取焦点,失去焦点案例 3 实现展示当前时间,定时功能 4 省市联动 二.jQuery入门 1 jQuery的两种导入方式 1.1 直接下载 ...
- python 并发专题(十一):基础部分补充(三)线程
1. 背景 理论上来说:单个进程的多线程可以利用多核. 但是,开发Cpython解释器的程序员,给进入解释器的线程加了锁. 2. 加锁的原因: 当时都是单核时代,而且cpu价格非常贵. 如果不加全局解 ...
- Django 【基础篇】
前戏 python Web程序 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #cod ...
- 理解js中的原型,原型对象,原型链
目录 理解原型 理解原型对象 实例属性与原型属性的关系 更简单的原型语法 原型的动态性 原型链 理解原型 我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象, ...
- echarts 实战 : 怎么写出和自动生成的一样的 tooltip ?
找到答案很麻烦,但答案本身很简单. 假设 需要给 echarts 的数据是 option. option.tooltip.formatter = (params) => { return `&l ...
- C#计算数组的算术平均数、几何平均数、调和平均数、平方平均数和中位数
1.函数实现 0)打印数组 /// <summary> /// 打印数组 /// </summary> /// <param name="arr"&g ...
- ajax工作原理/实例
ajax是什么? 是一种创建交互式网页应用的一种网页技术.简单来说,就是向服务器发起请求,获得数据使交互性和用户体验更好. ajax不是一种新的技术,是一些技术的集合体.有 1.XHTML和CSS 2 ...
- Java基础(一)基础常识
Java开发基础流程图: 也可参考这篇博客: https://www.cnblogs.com/xdp-gacl/p/3624567.html 常用的Windows的DOS命令 : dir : 列出当前 ...
- 小白必看,Python入门你要懂那些
Python作为为数不多的全场景开发语言之一,近年来已经获得了越来越多人的关注,而整个IT行业也释放出了大量的Python就业岗位.因此,当前学习Python语言是非常不错的选择,文泽带你进一步走进P ...