ApiVersion接口版本管理

ApiVersion配置相关类

WebConfig

在WebConfig(implements WebMvcRegistrations)里:

@Override
@NonNull
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
    return new ApiVersionRequestMappingHandlerMapping(ApiVersionKey.of(apiProperties.getVersion()));
}

一、使用方式

  1. 在Controller上注解:
@Api(value="Redis", tags="[Car]")
@ApiVersion("v12")
@RestController
@RequestMapping("{version}/redis")
public class RedisController {}

这里的{version}仅作占位使用,使之能识别一个参数,实际该参数在工程中并未被使用,以下是如何使用该参数:

@RequestMapping("/{version}/redis")
public String getData(@PathVariable String version){}
  1. 在方法上注解
@ApiVersion("v15")
@ApiOperation("publish message")
@ApiOperationSupport(ignoreParameters = {"id"})
@GetMapping("/pub")
public String publish(String channel, String body) {
    redisTemplateString.convertAndSend(channel, body);
    return "ok";
}

这样/v15/redis/pub将可以被spring识别。

二、无法匹配大于个位数的版本号

现有正则串只支持匹配一位版本号,即只能匹配/v1.3.4/,原pattern:

Pattern VERSION_PREFIX_PATTERN_1 = Pattern.compile("/v\\d\\.\\d\\.\\d/");

Pattern VERSION_PREFIX_PATTERN_2 = Pattern.compile("/v\\d\\.\\d/");

Pattern VERSION_PREFIX_PATTERN_3 = Pattern.compile("/v\\d/");

修改为:

private static final Pattern VERSION_PREFIX_PATTERN_1 = Pattern.compile("/v\\d+\\.\\d+\\.\\d+/");
/**
 * version format, 2 levels, v1.1 e.g.
 */
private static final Pattern VERSION_PREFIX_PATTERN_2 = Pattern.compile("/v\\d\\+.\\d+/");
/**
 * version format, 3 levels, v1.1.1 e.g.
 */
private static final Pattern VERSION_PREFIX_PATTERN_3 = Pattern.compile("/v\\d+/");

使之可以匹配多个数字,如匹配/v103.413.4342/。

三、当两个方法有相同路由,且同时被ApiVersion注解时

@ApiVersion("v16")
@GetMapping("/pub")
public String publish1(String channel, String body) {
    redisTemplateString.convertAndSend(channel, body);
    return "ok";
}
@ApiVersion("v15")
@ApiOperation("publish message")
@ApiOperationSupport(ignoreParameters = {"id"})
@GetMapping("/pub")
public String publish2(String channel, String body) {
    redisTemplateString.convertAndSend(channel, body);
    return "ok";
}

在这里插入图片描述

这样注册时/{version}/pub的version被设置为v16,latestVersion为yaml配置中config.api.version的值,version为实际请求的值。

经测试,将两个方法定义的顺序交换,该api版本变为v15,也就是原api管理的代码没有实现识别最新版本接口的功能(应完善ApiVersionRequestMappingHandlerMapping类)。

在这里插入图片描述

三、多版本选择逻辑

目前版本选择涉及到三个版本定义的比较:

  • yaml文件中配置的config.api.version,在compareVersion中该变量名为latestVersion

  • 由@ApiVersion定义的接口version,在compareVersion中该变量名为apiVersion

  • 请求url中带的version,在conpareVersion中该变量名为version
    请求version需满足比apiVersion大,且比latestVersion小,不然会返回404,比如以下是有效请求:

  • config.api.version=v20.0.0

  • 接口@ApiVersion(“v15”)

  • 请求url:/v18/pub
    以下是无效请求:

  • 请求url version小于ApiVersion

    • config.api.version=v20.0.0
    • 接口@ApiVersion(“v15.0”)
    • 请求url:/v12/pub
  • ApiVersion大于config.api.version

    • config.api.version=v2.0
    • 接口@ApiVersion(“v15”)
    • 请求url:/v12/pub
      版本号是以点分割,以v开头以点连接的1~3个数字,按位比较,逐次从高到低位比较,可以兼容三种pattern。

四、多模块版本管理

创建了三个模块,其中有公共配置模块shared-lib和依赖shared-lib的app1和app2,具体依赖到shared-lib中以下五个文件:

  • ApiVersion.java
  • ApiVersionCondition.java
  • ApiVersionKey.java
  • ApiVersionRequestMappingHandlerMapping.java
  • ApiProperties.java
  • WebConfig.java
    其中ApiProperties为获取propertise的Data,各模块的properties文件仅该模块可见,且后端服务端口不同,api version可共用代码,但各管各模块的。
各模块配置和试验结果如下
  • shared-lib:
    • config.api.version=v10.0.0
  • app1:8080端口
    • config.api.version=v9.0.0
    • @ApiVersion(“v2”)
    • 结果:v1:404,v2:ok,v8:ok,v9:ok,v10:404
  • app2:8081端口
    • config.api.version=v12.0.0
    • @ApiVersion(“v3”)
    • 结果:v2:404,v3:ok,v10:ok,v12:ok,v13:404
      结论:shard-lib配置的properties没有起作用,各模块执行代码使用的各自的上下文和method实体。

如何配置共用config.api.version

方法一:(失败)

子模块会覆盖父模块的properties,所以新增一条子模块配置项,改下相应的代码就可以了,重点是要明确api版本比较的逻辑,因为增加了一项需要比较的。

  1. 在子模块中新增配置项:config.api.childVersion
  2. 在ApiProperties.java增加一项:
private String childVersion;
  1. 修改ApiVersionCondition.java,暂定如果子模块version大于父模块定义的全局version,则在该子模块中覆盖,否则取全局version作为latestVersion
//新增
@Getter
private final ApiVersionKey childVersion;

//修改参数
@Override
@NonNull
public ApiVersionCondition combine(@NonNull ApiVersionCondition other) {
    return new ApiVersionCondition(other.apiVersion, latestVersion, childVersion);
}

//修改比较逻辑
@Override
public ApiVersionCondition getMatchingCondition(@NonNull HttpServletRequest request) {
    for (Pattern pattern : VERSION_LIST) {
        Matcher m = pattern.matcher(request.getRequestURI());
        if (m.find()) {
            ApiVersionKey version = ApiVersionKey.of(m.group(0).replace("/", StringConstants.EMPTY));
            ApiVersionKey compVersion = compareVersion(this.latestVersion, this.childVersion) > 0? this.latestVersion: this.childVersion;
            if (compareVersion(version, this.apiVersion) >= 0 && compareVersion(version, compVersion) <= 0) {
                return this;
            }
        }
    }
    return null;
}
  1. 同步修改WebConfig.java
@Override
@NonNull
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
    return new ApiVersionRequestMappingHandlerMapping(ApiVersionKey.of(apiProperties.getVersion()), ApiVersionKey.of(apiProperties.getChildVersion()));
}
  1. 同步修改ApiVersionRequestMappingHandlerMapping.java
@Getter
private final ApiVersionKey childVersion;

public ApiVersionRequestMappingHandlerMapping(final ApiVersionKey maxVersion, final ApiVersionKey childVersion) {
    this.latestVersion = maxVersion;
    this.childVersion = childVersion;
}
  1. 需要使子模块启动时,ApiVersion的bean能同时加载到父模块和子模块的properties,网上说mvn install父模块即可,但是这样尝试之后仍获取不到父模块version,如果需要采用这个方法的话还需探明启动子模块时ApiVersion bean初始化方法。
方法二:(成功)

直接在ApiVersionKey.java中配置final项指定default_version,以后修改全局项改该成员变量:

public static final ApiVersionKey DEFAULT_VERSION = new ApiVersionKey(10, 0, 0);
  1. 测试结果成功
  • shared-lib:
    • DEFAULT_VERSION=v10.0.0
  • app1:8080端口
    • config.api.childVersion=v9.0.0
    • @ApiVersion(“v2”)
    • 结果:v1:404,v2:ok,v8:ok,v9:ok,v10:ok
  • app2:8081端口
    • config.api.childVersion=v12.0.0
    • @ApiVersion(“v3”)
    • 结果:v2:404,v3:ok,v10:ok,v12:ok,v13:404
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Swagger是一款流行的API设计工具,它可以帮助我们更轻松地设计、构建和测试API。Swagger2是Swagger的一个版本,它可以生成API接口文档,这样我们就可以更清晰地了解API的使用方法。 下面是使用Swagger2生成API接口文档的步骤: 1. 在项目中添加Swagger2依赖 在Maven项目中,可以在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> ``` 2. 配置Swagger2 在Spring Boot项目中,可以在application.properties或application.yml文件中添加以下配置: ``` # Swagger2配置 swagger2.enabled=true # 启用Swagger2 swagger2.title=API接口文档 # 文档标题 swagger2.description=API接口文档 # 文档描述 swagger2.version=1.0 # 文档版本 swagger2.basePackage=com.example.demo # 扫描的包路径 ``` 3. 编写API接口 在Spring Boot项目中,可以使用Spring MVC的注解来编写API接口,例如: ``` @RestController @RequestMapping("/api") @Api(tags = "用户管理") public class UserController { @GetMapping("/users") @ApiOperation("获取用户列表") public List<User> getUsers() { // TODO: 查询用户列表 return null; } @GetMapping("/users/{id}") @ApiOperation("根据ID获取用户") @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long") public User getUserById(@PathVariable Long id) { // TODO: 根据ID查询用户 return null; } @PostMapping("/users") @ApiOperation("创建用户") @ApiImplicitParam(name = "user", value = "用户信息", required = true, dataType = "User") public User createUser(@RequestBody User user) { // TODO: 创建用户 return null; } @PutMapping("/users/{id}") @ApiOperation("更新用户") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long"), @ApiImplicitParam(name = "user", value = "用户信息", required = true, dataType = "User") }) public User updateUser(@PathVariable Long id, @RequestBody User user) { // TODO: 更新用户 return null; } @DeleteMapping("/users/{id}") @ApiOperation("删除用户") @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long") public void deleteUser(@PathVariable Long id) { // TODO: 删除用户 } } ``` 4. 访问Swagger2文档 在启动Spring Boot应用程序后,可以在浏览器中访问以下URL以查看Swagger2生成的API接口文档:http://localhost:8080/swagger-ui.html 在Swagger2的文档页面中,可以查看接口的详细信息,包括请求和响应的参数、请求方式、请求路径等。同时,我们也可以在页面中测试API接口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值