如何优雅的设计 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 ...
随机推荐
- 结合实际需求,在webapi内利用WebSocket建立单向的消息推送平台,让A页面和服务端建立WebSocket连接,让其他页面可以及时给A页面推送消息
1.需求示意图 2.需求描述 原本是为了给做unity3d客户端开发的同事提供不定时的消息推送,比如商城购买道具后服务端将道具信息推送给客户端. 本篇文章简化理解,用“相关部门开展活动,向全市人民征集 ...
- MySQL CodeFirst的配置与注意事项
mysql+ef的配置相比较mssql+ef来说复杂一些.我的感受就是配置难度在于插件版本造成的各种不兼容问题.另外参考了很多博客,将多个博客里的经验综合才得以实现,因为不是每个人的操作都和那些博客作 ...
- css3 文本行的斑马线
背景知识 CSS 渐变, background-size ,“条纹背景”,“灵活的背景定位 难题 几年前,在刚刚获得 :nth-child() / :nth-of-type() 伪类之后,我们最常用其 ...
- Java标识符/数据类型,规范等详解
Java标识符 类名/变量名/方法名都称之为标识符. Java 所有的组成部分都需要名字.类名.变量名以及方法名都被称为标识符. 关于 Java 标识符,有以下几点需要注意: 所有的标识符都应该以字母 ...
- 根据URL下载图片到本地
/// <summary> /// 下载图片 /// </summary> /// <param name="picUrl">图片Http地址& ...
- sql 增加修改表格属性字段
1.修改字段默认值 alter table 表名 drop constraint 约束名字 ------注解:删除表的字段的原有约束 alter table 表名 add constraint 约束名 ...
- CRLF injection 简单总结
CRLF injection 简单总结 简介 CRLF是"回车 + 换行"(\r\n)的简称,即我们都知道在HTTP协议中,HTTP Header与HTTP Body是用两个CRL ...
- Unity-ECS(一)浅谈CPU缓存命中和Unity面向数据技术栈(DOTS)--笔记
一,缓存类型 概念:局部性. 时间局部性:当前用到的一个存储器位置,不久的将来会被用到. 空间局部性:当前用到的一个存储器位置,附近的位置会被用到. 那么在CPU的层面,这两个局部性的特性就会被Cac ...
- 集训 T4-分配时间
题目: 思路: 这个题目正解为dp,但是我并不会dp,所以写了个类似于T3的搜索.(然后就70分了 先看一张图: 我的思路是把写名字的时间和写卷子的时间算在了一起(下标表示时间点,比如下标2那一行代表 ...
- 从0搭建一个基于 ELK 的日志、指标收集与监控系统
为了使得私有化部署的系统能更健壮,同时不增加额外的部署运维工作量,本文提出了一种基于 ELK 的开箱即用的日志和指标收集方案. 在当前的项目中,我们已经使用了 Elasticsearch 作为业务的数 ...