当前位置: 首页 > news >正文

【Spring Boot】构建RESTful服务 — 实战:实现Web API版本控制

实战:实现Web API版本控制

前面介绍了Spring Boot如何构建RESTful风格的Web应用接口以及使用Swagger生成API的接口文档。如果业务需求变更,Web API功能发生变化时应该如何处理呢?可以通过Web API的版本控制来处理。

1.为什么进行版本控制

一般来说,Web API是提供给其他系统或其他公司使用的,不能随意频繁地变更。然而,由于需求和业务不断变化,Web API也会随之不断修改。如果直接对原来的接口修改,势必会影响其他系统的正常运行。

例如,系统中用户添加的接口/api/user由于业务需求的变化,接口的字段属性也发生了变化,而且可能与之前的功能不兼容。为了保证原有的接口调用方不受影响,只能重新定义一个新的接口:/api/user2,这使得接口维护难度增加。

如何做到在不影响现有调用方的情况下,优雅地更新接口的功能呢?

最简单高效的办法就是对Web API进行有效的版本控制。通过增加版本号来区分对应的版本,来满足各个接口调用方的需求。版本号的使用有以下几种方式:

1)通过域名进行区分,即不同的版本使用不同的域名,如v1.api.test.com、v2.api.test.com。

2)通过请求URL路径进行区分,在同一个域名下使用不同的URL路径,如test.com/api/v1/、test.com/api/v2。

3)通过请求参数进行区分,在同一个URL路径下增加version=v1或v2等,然后根据不同的版本选择执行不同的方法。

在实际项目开发中,一般选择第二种方式,因为这样既能保证水平扩展,又不影响以前的老版本。

2.Web API的版本控制

Spring Boot对RESTful的支持非常全面,因而实现RESTful API非常简单,同样对于API版本控制也有相应的实现方案:

1)创建自定义的@APIVersion注解。

2)自定义URL匹配规则ApiVersionCondition。

3)使用RequestMappingHandlerMapping创建自定义的映射处理程序,根据Request参数匹配符合条件的处理程序。

下面通过示例程序来演示Web API如何增加版本号。

步骤01 创建自定义注解。

创建一个自定义版本号标记注解@ApiVersion。实现代码如下:

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface ApiVersion {/*** @return版本号*/int value() default 1;}

在上面的示例中,创建了ApiVersion自定义注解用于API版本控制,并返回了对应的版本号。

步骤02 自定义URL匹配逻辑。

接下来定义URL匹配逻辑,创建ApiVersionCondition类并继承RequestCondition接口,其作用是进行版本号筛选,将提取请求URL中的版本号与注解上定义的版本号进行对比,以此来判断某个请求应落在哪个控制器上。实现代码如下:

public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile(".*v(\\d).*");private int apiVersion;ApiVersionCondition(int apiVersion) {this.apiVersion = apiVersion;}private int getApiVersion() {return apiVersion;}@Overridepublic ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {return new ApiVersionCondition(apiVersionCondition.getApiVersion());}@Overridepublic ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {Matcher m = VERSION_PREFIX_PATTERN.matcher(httpServletRequest.getRequestURI());if (m.find()) {Integer version = Integer.valueOf(m.group(1));if (version>=this.apiVersion) {return this;}}return null;}@Overridepublic int compareTo(ApiVersionCondition apiVersionCondition, HttpServletRequest httpServletRequest) {return apiVersionCondition.getApiVersion() - this.apiVersion;}
}

在上面的示例中,通过ApiVersionCondition类重写RequestCondition定义URL匹配逻辑。

当方法级别和类级别都有ApiVersion注解时,通过ApiVersionRequestCondition.combine方法将二者进行合并。最终将提取请求URL中的版本号,与注解上定义的版本号进行对比,判断URL是否符合版本要求。

步骤03 自定义匹配的处理程序。

接下来实现自定义匹配的处理程序。先创建ApiRequestMappingHandlerMapping类,重写部分RequestMappingHandlerMapping的方法,实现自定义的匹配处理程序。示例代码如下:

public class ApiRequestMappingHandlerMapping extends RequestMappingHandlerMapping {private static final String VERSION_FLAG = "{version}";private static RequestCondition<ApiVersionCondition> createCondition(Class<?> clazz) {RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);if (classRequestMapping == null) {return null;}StringBuilder mappingUrlBuilder = new StringBuilder();if (classRequestMapping.value().length > 0) {mappingUrlBuilder.append(classRequestMapping.value()[0]);}String mappingUrl = mappingUrlBuilder.toString();if (!mappingUrl.contains(VERSION_FLAG)) {return null;}ApiVersion apiVersion = clazz.getAnnotation(ApiVersion.class);return apiVersion == null ? new ApiVersionCondition(1) : new ApiVersionCondition(apiVersion.value());}@Overrideprotected RequestCondition<?> getCustomMethodCondition(Method method) {return createCondition(method.getClass());}@Overrideprotected RequestCondition<?> getCustomTypeCondition(Class<?> handerType) {return createCondition(handerType);}
}

步骤04 配置注册自定义的RequestMappingHandlerMapping。

创建WebMvcRegistrationsConfig类,重写getRequestMappingHandlerMapping( )的方法,将之前创建的ApiRequestMappingHandlerMapping注册到系统中。

public class WebMvcRegistrationsConfig implements WebMvcRegistrations {@Overridepublic RequestMappingHandlerMapping getRequestMappingHandlerMapping() {return new ApiRequestMappingHandlerMapping();}
}

通过以上4步完成API版本控制的配置。代码看起来复杂,其实都是重写Spring Boot内部的处理流程。

步骤05 配置实现接口。

配置完成之后,接下来编写测试的控制器(Controller),实现相关接口的测试。在Controller目录下分别创建OrderV1Controller和OrderV2Controller。示例代码如下:

//V1 版本的接口定义
@ApiVersion(value = 1)
@RestController
@RequestMapping("/api/{version}/order")
public class OrderV1Controller {@GetMapping("/delete/{orderId}")public JSONResult deleteOrderById(@PathVariable String orderId) {System.out.println("V1删除订单成功:" +orderId);return JSONResult.ok("V1删除订单成功");}@GetMapping("/detail/{orderId}")public JSONResult deleteOrderById(@PathVariable String orderId) {System.out.println("V1获取订单详情成功:" +orderId);return JSONResult.ok("V1获取订单详情成功");}
}//V2 版本的接口定义
@ApiVersion(value = 2)
@RestController
@RequestMapping("/api/{version}/order")
public class OrderV2Controller {@GetMapping("/delete/{orderId}")public JSONResult deleteOrderById(@PathVariable String orderId) {System.out.println("V2获取订单详情成功:" +orderId);return JSONResult.ok("V2获取订单详情成功");}@GetMapping("/list")public JSONResult list() {System.out.println("V2,获取list订单列表接口");return JSONResult.ok(200, "V2,新增list订单列表接口");}
}

在上面的示例中,我们在UserV1Controller中定义了/delete/{orderId}和/detail/{orderId}两个接口,在UserV2Controller中修改/detail/{orderId}接口,新增/list接口,然后使用@ApiVersion自定义注解设置两个Controller的版本号。

步骤06 验证测试。

配置完成之后启动项目,查看版本控制是否生效。在浏览器中分别访问api/v1/order/delete/20011和api/v2/order/ delete/20011订单删除接口,查看页面返回情况。如下图所示,调用V1和V2版本的order/ delete/20011订单删除接口,返回的都是“V1,删除订单成功”。这说明V2会默认继承V1的所有接口,新版本的原有接口功能保持不变。

在这里插入图片描述

接下来,在浏览器中分别访问api/v1/order/detail/20011和api/v2/order/delete/20011订单详情接口,查看页面返回情况。如下图所示,分别调用V1和V2版本的order/detail/20011订单详情接口,返回的是各自版本的接口信息,说明V2版本对order/detail订单详情接口的修改生效,同时也没有影响旧版本的订单详情接口。

在这里插入图片描述
在这里插入图片描述
最后,分别访问新增的order/list订单列表接口,查页面返回情况。如下图所示,请求V1的order/list订单列表返回404接口不存在,请求V2的order/list订单列表返回正确的结果,说明在高版本中新增的接口只在高版本中生效。
在这里插入图片描述

在这里插入图片描述
以上验证情况说明Web API的版本控制配置成功,实现了旧版本的稳定和新版本的更新。

1)当请求正确的版本地址时,会自动匹配版本的对应接口。

2)当请求的版本大于当前版本时,默认匹配最新的版本。

3)高版本会默认继承低版本的所有接口。实现版本升级只关注变化的部分,没有变化的部分会自动平滑升级,这就是所谓的版本继承。

4)高版本的接口的新增和修改不会影响低版本。

这些特性使得在升级接口时,原有接口不受影响,只关注变化的部分,没有变化的部分自动平滑升级。这样使得Web API更加简洁,这就是实现Web API版本控制的意义所在。

相关文章:

【Spring Boot】构建RESTful服务 — 实战:实现Web API版本控制

实战&#xff1a;实现Web API版本控制 前面介绍了Spring Boot如何构建RESTful风格的Web应用接口以及使用Swagger生成API的接口文档。如果业务需求变更&#xff0c;Web API功能发生变化时应该如何处理呢&#xff1f;可以通过Web API的版本控制来处理。 1.为什么进行版本控制 …...

6.Web后端开发【SpringBoot入门】

文章目录 1 SpringBoot快速入门1.1 Web分析 2. HTTP协议2.1 HTTP-概述2.1.1 介绍2.2.2 特点 2.2 HTTP-请求协议2.3 HTTP-响应协议2.3.1 格式介绍2.3.2 响应状态码 常见的相应状态码 3 WEB服务器3.1 服务器概述 1 SpringBoot快速入门 Spring的官网Spring Boot 可以帮助我们非常…...

[ubuntu]ubuntu18.04使用自带共享桌面实现vncserver连接

vncserver有很多方法比如你安装vnc4server,tightvncserver,x11vnc等都可以实现vnc局域网连接&#xff0c;今天使用系统共享桌面设置vnc连接 Ubuntu开启远程桌面 Ubuntu18.04使用gnome桌面环境&#xff0c;系统自带屏幕共享和远程登录功能&#xff0c;默认使用的是vino作为VNC…...

docker启用cgroup v2

要求 本人的操作系统是kali&#xff0c;基于debian docker info如果你这里是2那么说明启用了&#xff0c;如果是1&#xff0c;那么就未启用 对于Docker来说&#xff0c;Cgroups v2的使用需要满足以下条件&#xff1a; Linux内核版本在4.15以上。 uname -r 系统已经启用Cgro…...

Java课题笔记~ Axios

Axios 对原生的AJAX进行封装&#xff0c;简化书写。 Axios官网是&#xff1a;https://www.axios-http.cn 2.1 基本使用 axios 使用是比较简单的&#xff0c;分为以下两步&#xff1a; 引入 axios 的 js 文件 <script src"js/axios-0.18.0.js"></script…...

ip地址和地理位置有关系吗

在互联网时代&#xff0c;网络已经成为了人们生活中不可或缺的一部分。而在网络通信中&#xff0c;IP地址扮演着非常重要的角色。那么&#xff0c;IP地址和地理位置之间是否有关系呢&#xff1f;虎观代理小二二将从以下几个方面进行探讨。 一、IP地址和地理位置的基本概念 首…...

mac指定node版本 mac node版本降级 mac切换node版本

本文解决问题&#xff1a; mac指定node版本 mac切换node版本 mac node版本降级 第一步 进行nvm 安装操作 brew install nvm 执行 nvm --version nvm --version 出现zsh: command not found: nvm问题去进行配置第二步 nvm配置 1.输入: vim ~/.bash_profile 点击 i 进行插入…...

C# Windows登录界面进行截图,控制鼠标键盘等操作实现(一)

首先常规的账户进程是没办法获取登录界面的信息的&#xff0c;因为登录界面已经不在某个账户下了&#xff0c;登录界面显示了每一个账户的切换。所以得使用System权限的进程。 那么Windows系统究竟是怎么将登录界面与用户桌面隔离开的呢&#xff1f;首先先通过一些Windows操作系…...

因果推断(五)基于谷歌框架Causal Impact的因果推断

因果推断&#xff08;五&#xff09;基于谷歌框架Causal Impact的因果推断 除了传统的因果推断外&#xff0c;还有一些机器学习框架可以使用&#xff0c;本文介绍来自谷歌框架的Causal Impact。该方法基于合成控制法的原理&#xff0c;利用多个对照组数据来构建贝叶斯结构时间…...

VR全景加盟项目如何开展?如何共赢VR时代红利?

VR全景作为一个新兴蓝海项目&#xff0c;相信有着很多人刚接触VR行业的时候都会有这样的疑问&#xff1a;VR全景加盟后项目如何开展&#xff1f;今天&#xff0c;我们就从项目运营的三个阶段为大家讲解。 一、了解项目时 目前VR全景已经被应用到各行各业中去&#xff0c;学校、…...

Win10+anaconda+CUDA+pytorch+vscode配置

Win10anacondaCUDApytorchvscode配置 1.安装anaconda2.安装CUDA确认CUDA版本确认CUDA和pytorch版本安装CUDA 3.安装cudnn4.安装Pytorch5.vscode配置安装VScodevscode配置pytorch环境 1.安装anaconda 官网https://www.anaconda.com 下载安装&#xff0c;路径全英文然后记得有一…...

vue-router在vue2/3区别

构建选项区别 vue2-router const router-new VueRouter({mode:history,base:_name,})vue-next-router import { createRouter,createWebHistory} from vue-next-router const routercreateRouter({history:createHistory(/) })在上述代码中我们发现,vue2中的构建选项mode和ba…...

Apache Doris 入门教程33:统计信息

统计信息 统计信息简介​ Doris 查询优化器使用统计信息来确定查询最有效的执行计划。Doris 维护的统计信息包括表级别的统计信息和列级别的统计信息。 表统计信息&#xff1a; 信息描述row_count表的行数data_size表的⼤⼩&#xff08;单位 byte&#xff09;update_rows收…...

有效需求的特征

如何区分优秀的软件需求和软件需求规格说明书&#xff08;SRS&#xff09;与可能导致问题的需求和规格说明书&#xff1f;在这篇文章中&#xff0c;我们将首先讨论单个需求应该具有的几种不同特性。然后&#xff0c;我们将讨论成功的SRS整体应具有的理想特征。 1.有效需求的特…...

基于51单片机无线温度报警控制器 NRF24L01 多路温度报警系统设计

一、系统方案 1、本设计默认采用STC89C52单片机&#xff0c;如需更换单片机请联系客服。 2、接收板LCD1602液晶实时显示当前检测的2点温度值以及对应的上下限报警值。发射板由DS18B20采集温度值&#xff0c;通过无线模块NRF24L01传给接收板。 3、按键可以设置温度上下限值&…...

Spring Data JPA的@Entity注解

一、示例说明 rules\CouponTypeConverter.java Converter public class CouponTypeConverterimplements AttributeConverter<CouponType, String> {Overridepublic String convertToDatabaseColumn(CouponType couponCategory) {return couponCategory.getCode();}Overr…...

CANoe panel中,Path Dialog如何保存选择的文件路径

这里写目录标题 Path Dialog控件的设置系统变量和环境变量 Path Dialog控件的设置 过滤加载的文件类型 填写格式为&#xff1a;Hex file |.hex 其中Hex file为自定义name&#xff0c;.hex为你想识别的文件类型 系统变量和环境变量 系统变量&#xff1a;在canoe的Environmen…...

关于es中索引,倒排索引的理解

下面是我查询进行理解的东西 也就是说我们ES中的索引就相当于我们mysql中的数据库表&#xff0c;索引库就相当于我们的数据库&#xff0c;我们按照mapping规则会根据相应的字段&#xff08;index为true默认&#xff09;来创建倒排索引&#xff0c;这个倒排索引就相当于我们索引…...

k8s service (二)

K8s service (二) Endpoint Endpoint是kubernetes中的一个资源对象&#xff0c;存储在etcd中&#xff0c;用来记录一个service对应的所有pod访问地址&#xff0c;它是根据service匹配文件中selector描述产生的。 一个Service由一组Pod组成&#xff0c;这些Pod通过Endpoints…...

桌面软件开发框架 Electron、Qt、WPF 和 WinForms 怎么选?

一、Electron Electron 是一个基于 Web 技术的跨平台桌面应用程序开发框架。它使用 HTML、CSS 和 JavaScript 来构建应用程序界面,并借助 Chromium 渲染引擎提供强大的页面渲染能力。Electron 的主要特点包括: 跨平台:Electron 可以在 Windows、macOS 和 Linux 等多个主流操…...

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第二天)Mybatis的深入学习

SSM框架的学习与应用(Spring Spring MVC MyBatis)-Java EE企业级应用开发学习记录&#xff08;第二天&#xff09;Mybatis的深入学习&#xff08;增删改查的操作&#xff09; 上一篇我们的项目搭建好了&#xff0c;也写了简答的Junit测试类进行测试&#xff0c;可以正确映射…...

学习笔记:Opencv实现限制对比度得自适应直方图均衡CLAHE

2023.8.19 为了完成深度学习的进阶&#xff0c;得学习学习传统算法拓展知识面&#xff0c;记录自己的学习心得 CLAHE百科&#xff1a; 一种限制对比度自适应直方图均衡化方法&#xff0c;采用了限制直方图分布的方法和加速的插值方法 clahe&#xff08;限制对比度自适应直方图…...

R语言处理缺失数据(1)-mice

#清空 rm(listls()) gc()###生成模拟数据### #生成100个随机数 library(magrittr) set.seed(1) asd<-rnorm(100, mean 60, sd 10) %>% round #平均60&#xff0c;标准差10 #将10个数随机替换为NA NA_positions <- sample(1:100, 10) asd[NA_positions] <- NA #转…...

SpringBoot自动配置原理

Spring Boot 的自动配置可以根据添加的jar依赖&#xff0c;自动配置 Spring Boot 应用程序。例如&#xff0c;我们想要使用Redis&#xff0c;直接在POM文件中增加spring-boot-starter-data-redis依赖&#xff0c;然后我们配置下连接信息就可以使用了。 那么Spring Boot 是如何…...

HarmonyOS学习路之方舟开发框架—学习ArkTS语言(状态管理 五)

管理应用拥有的状态概述 LocalStorage&#xff1a;页面级UI状态存储 LocalStorage是页面级的UI状态存储&#xff0c;通过Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例。LocalStorage也可以在UIAbility内&#xff0c;页面间共享状态。 本文仅介绍LocalStora…...

Java基础篇——反射枚举

反射&枚举 课程目标 1. 【理解】类加载器 2. 【理解】什么是反射 3. 【掌握】获取Class对象的三种方式 4. 【掌握】反射获取构造方法并创建对象 5. 【掌握】反射获取成员变量并使用 6. 【掌握】反射获取成员方法并使用 7. 【掌握】反射综合案例 8. 【理解】枚举B友:http…...

每日一学——案例难点Windows配置

在Windows上配置DNS服务器有几个步骤&#xff1a; 步骤1&#xff1a;打开网络连接设置 在任务栏上右键单击网络图标&#xff0c;并选择“打开网络和Internet设置”。 在新窗口中&#xff0c;选择“更改适配器选项”。 在打开的窗口中&#xff0c;找到正在使用的网络适配器&a…...

2023.8 - java - 运算符

Java 运算符 算术运算符关系运算符位运算符逻辑运算符赋值运算符其他运算符 算术运算符 算术运算符用在数学表达式中&#xff0c;它们的作用和在数学中的作用一样。下表列出了所有的算术运算符。 表格中的实例假设整数变量A的值为10&#xff0c;变量B的值为20&#xff1a; …...

推荐三款Scrum敏捷项目管理工具/敏捷管理实践

免费版敏捷工具推荐&#xff1a; Leangoo领歌 Leangoo领歌是ScrumCN&#xff08;scrum.cn&#xff09;旗下的一款永久免费的专业敏捷开发管理工具&#xff0c;提供端到端敏捷研发管理解决方案&#xff0c;涵盖敏捷需求管理、任务协同、进展跟踪、缺陷管理、统计度量等。包括小…...

WARNING: undefined behavior - version of Delve is too old for Go version

在更新了 go 版本后&#xff0c;使用 goland 进行调试会报错 WARNING: undefined behavior - version of Delve is too old for Go version 1.20.5 (maximum supported version 1.19)这是因为 go 的版本升级后&#xff0c;相对 dlv 的版本就低了。 所以解决办法就是升级对应的…...

弹窗网站制作器/怎么做推广比较成功

可以使用CTRLC来终结python进程池(Multiprocessing Pool)的方法&#xff1a;原文以后有时间翻译一下吧#!/usr/bin/env python# Copyright (c) 2011 John Reese# Licensed under the MIT Licenseimport multiprocessingimport osimport signalimport timedef init_worker():sign…...

杭州网站建设专家/网推软件有哪些

方法一&#xff1a; 求余数 x%2 0 为偶数 方法二&#xff1a; 位运算 & x & 1 0为偶数。 原因&#xff1a;在计算机系统中&#xff0c;数值一律用补码来表示和存储。正整数的补码和原码相同。二进制转十进制时&#xff0c;可以表示成a*2^nb*2^n-1.....。其中最后一位…...

卖童书的网站该怎么做/合肥网

遇到一个需求&#xff0c;需要备份Access数据库&#xff0c;Access生成的数据都保存在xx.mdb文件中&#xff0c;所以考虑使用windows任务 定时执行一个备份文件的bat文件来解决这个问题。 backup.bat文件代码&#xff1a; echo off Set nowdate%date:~,10% copy E:\Testing\Dem…...

网上购物商城系统论文/关键词优化的作用

使用dict和set 阅读: 346206dict Python内置了字典&#xff1a;dict的支持&#xff0c;dict全称dictionary&#xff0c;在其他语言中也称为map&#xff0c;使用键-值&#xff08;key-value&#xff09;存储&#xff0c;具有极快的查找速度。 举个例子&#xff0c;假设要根据同学…...

计算机网站建设与开发/搜索引擎google

谢谢dudu给大家提供了这么好的一个技术和思想交流的场所&#xff0c;我本来开个帐号是用来和其他人沟通而已&#xff0c;但是发现有空整理一下思路&#xff0c;突然发现有太多要整理的了&#xff0c;三十多年怎么能没有一点看法呢&#xff1f;于是经常在坐车时总结、构思。已经…...

网站源码大全免费/网课培训机构排名前十

关注 M r . m a t e r i a l , \color{Violet} \rm Mr.material\ , Mr.material ,...