如何优雅的设计 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 ...
随机推荐
- Python——操作smb文件服务器(上传和下载)
最近在做上传和下载,然后文件比较大和多,就用到了文件服务器,文件服务器是实体机 ,不是在本地, 然后用python 通过pysmb模块就可以直接进行操作 mac选择前往.连接服务器去查看文件服务器里都 ...
- 2020/7/6博客日报Java的开始--pthread的安装
1.今天进行了Java环境的安装,安装eclipse之后学习了eclipse的使用方法,如何建立项目,并完成了最基本的Java hello的运行: 今天是Java的开端,开始进入Java编程: 今天了 ...
- linux专题(二):走近Linux系统 (2020-04-08 10:08)
http://dwz.date/UDf 走近Linux系统 开机登录 开机会启动许多程序.它们在Windows叫做"服务"(service),在Linux就叫做"守护进程 ...
- web CSS3 实现3D动态翻牌效果
使用纯CSS3 实现翻牌效果 需要注意要给子盒子使用绝对定位,这样两个盒子可以完全重合在一起,需要给父盒子一个 transform-style: preserve-3d;让子盒子翻转时保持3D效果, ...
- bzoj3384[Usaco2004 Nov]Apple Catching 接苹果*&&bzoj1750[Usaco2005 qua]Apple Catching*
bzoj3384[Usaco2004 Nov]Apple Catching 接苹果 bzoj1750[Usaco2005 qua]Apple Catching 题意: 两棵树,每分钟会从其中一棵树上掉 ...
- bzoj2132圈地计划
bzoj2132圈地计划 题意: 一块土地可以纵横划分为N×M块小区域.于第i行第j列的区域,建造商业区将得到Aij收益,建造工业区将得到Bij收益.而如果区域(i,j)相邻(相邻是指两个格子有公共边 ...
- C#递归的简单实例
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
- SAS X option
1. SAS X选项就是调用DOS命令. 例子: option noxwait;/*黑窗口执行完命令后自动关闭*/ %let path =.; %let filter=*.lst; X “ dir & ...
- Elasticsearch及相关插件的安装
Elasticsearch及相关插件的安装 1.安装Elasticsearch并启动服务 2.安装第三方插件 2.1.Head插件 是Elasticsearch的一个集群管理工具,可以通过它来查看和搜 ...
- php提取xml配置参数
demo1.php <?php class AddressManager{ private $addresses = array("ip地址1","ip地址2&qu ...