bigywater.tech
Spring · 168

Spring MVC 的 API 版本号:声明 version = "1.2+",请求 1.9 为什么还是 400

Spring Framework 7 给 @RequestMapping 加了 version 属性,"1.2+" 看起来应该接住 1.9,为什么实测还是 400?不带版本头、版本不在 supported 里、换策略之后分别是什么报错?这篇在 Spring Boot 4.0.8 上逐个请求。

1.9 → 400
映射写了 version = "1.2+",但 1.9 不在 supported 集合里时请求被拒,异常 InvalidApiVersionException
三种异常
InvalidApiVersionException、MissingApiVersionException、NotAcceptableApiVersionException,状态码都是 400
策略换了就看不到
用查询参数策略后,请求头 X-API-Version: 2.0 被忽略,返回 400

header 策略

实测 · 本机执行输出
  带 1.0:200 v1.0 方法
  带 1.1:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: '1.1.0'."
  带 1.2:200 v1.2+ 方法
  带 1.9:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: '1.9.0'."
  带 2.0:200 v2.0 方法
  带 3.0:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: '3.0.0'."
  带 abc:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: 'abc'."
  不带版本头:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.MissingApiVersionException: 400 BAD_REQUEST "API version is required."
  访问没写 version 的 /plain,带 2.0:200 没写 version 的方法
  访问没写 version 的 /plain,不带:400 {"status":400,"error":"Bad Request","path":"/plain"} ← /plain …accept.MissingApiVersionException: 400 BAD_REQUEST "API version is required."

1.0、1.2、2.0 命中,1.1、1.9、3.0、abc 与不带版本头都是 400;配了策略,没写 version 的 /plain 不带版本头也是 400。

supported 与 default

实测 · 本机执行输出
  带 1.0:200 v1.0 方法
  带 1.1(不在 supported 列表里):400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: '1.1.0'."
  带 1.2:200 v1.2+ 方法
  带 1.9:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: '1.9.0'."
  带 3.0:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: '3.0.0'."
  不带版本头:200 v1.0 方法

default=1.0 让不带版本头的请求命中 1.0 方法;supported 的内容与自动收集的相同时,结果不变。

补全 supported

实测 · 本机执行输出
  带 1.1:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.NotAcceptableApiVersionException: 400 BAD_REQUEST "Invalid API version: '1.1.0'."
  带 1.9:200 v1.2+ 方法
  带 3.0:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.NotAcceptableApiVersionException: 400 BAD_REQUEST "Invalid API version: '3.0.0'."

补上 1.9 后命中 1.2+ 方法;1.1 与 3.0 变成 NotAcceptableApiVersionException。

required=false

实测 · 本机执行输出
  带 1.0:200 v1.0 方法
  带 1.1:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: '1.1.0'."
  带 3.0:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: '3.0.0'."
  不带版本头:200 v2.0 方法

不带版本头也是 200,命中 v2.0 方法,原因没有深究。

查询参数

实测 · 本机执行输出
  /hello?v=2.0:200 v2.0 方法
  /hello?v=1.2:200 v1.2+ 方法
  /hello 不带参数:400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.MissingApiVersionException: 400 BAD_REQUEST "API version is required."
  header 带 2.0(策略已不是 header):400 {"status":400,"error":"Bad Request","path":"/hello"} ← /hello …accept.MissingApiVersionException: 400 BAD_REQUEST "API version is required."

换策略后请求头被忽略,不带参数是 400。

路径段

实测 · 本机执行输出
  /api/1.0/hello:200 v1.0 方法
  /api/2.0/hello:200 v2.0 方法
  /api/3.0/hello:400 {"status":400,"error":"Bad Request","path":"/api/3.0/hello"} ← /api/3.0/hello …accept.InvalidApiVersionException: 400 BAD_REQUEST "Invalid API version: '3.0.0'."
  /api/v2/hello:200 v2.0 方法

/api/{version}/hello,/api/v2/hello 命中 v2.0 方法。

分步演示

API版本
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动(Spring Boot 4.0.8,Spring Framework 7.0.9,spring-boot-starter-webmvc;三个 GET /hello 映射声明 version = "1.0"、"1.2+"、"2.0";每种配置启动一次应用后依次请求;异常类名由一个最高优先级的 HandlerExceptionResolver 记录;spring.mvc.apiversion.* 属性名取自 spring-boot-webmvc 4.0.8 的配置元数据)
    • 没有验证:required=false 且没有 default 时为什么选中 2.0 方法;WebFlux 下的行为;media-type-parameter 策略(Accept 头里带版本);ApiVersionDeprecationHandler 与弃用头;用 WebMvcConfigurer.configureApiVersioning 编程式配置与属性配置的差别;1.2+ 之外的区间写法;Spring Security、Gateway 前置时版本头是否被保留
    京ICP备2026067502号-1京公网安备11010502063487号