bigywater.tech
Spring · 41

参数解析:HandlerMethodArgumentResolver 与 @RequestParam、@PathVariable、@RequestBody

各种参数注解遇到缺失、类型错、JSON 坏时分别返回什么?集合怎么传?不带注解的参数怎么处理?对象绑定有什么坑?这篇在 Spring Boot 4.0.8 的 Spring MVC 7.0.9 上用一组请求逐个验证。

500
不带注解的 int 参数缺失时的状态码(IllegalStateException)
0 个字段
只有 public 字段、没有 setter 的对象通过表单绑定到的字段数(不报错)
400
必需参数缺失、类型转换失败、JSON 解析失败各自返回的状态码

@RequestParam

实测 · 本机执行输出
  GET /p1?q=hi → 200 p1 q=hi
  GET /p1 → 400 MissingServletRequestParameterException: Required request parameter 'q' for method parameter type String is not present
  GET /p2 → 200 p2 q=null
  GET /p3 → 200 p3 q=dft
  GET /p3?q= → 200 p3 q=dft
  GET /p4?n=7 → 200 p4 n=Optional[7]
  GET /p4 → 200 p4 n=Optional.empty
  GET /p5?n=abc → 400 MethodArgumentTypeMismatchException: Method parameter 'n': Failed to convert value of type 'java.lang.String' to required type 'int'; For input string: "abc"
  GET /p5 → 400 MissingServletRequestParameterException: Required request parameter 'n' for method parameter type int is not present
  GET /p9?flag=yes → 200 p9 flag=true
  GET /p9?flag=on → 200 p9 flag=true
  GET /p9?flag=maybe → 400 MethodArgumentTypeMismatchException: Method parameter 'flag': Failed to convert value of type 'java.lang.String' to required type 'boolean'; Invalid boolean value [maybe]

必需、可选、默认值、Optional;缺失和类型转换失败都是 400。

集合、Map 与路径变量

实测 · 本机执行输出
  GET /p6?a=1&a=2 → 200 p6 a=[1, 2]
  GET /p6?a=1,2 → 200 p6 a=[1, 2]
  GET /p7?x=1&y=2&x=3 → 200 p7 all={x=1, y=2}
  GET /p8?implicit=a&num=5 → 200 p8 implicit=a,num=5
  GET /p8?implicit=a → 500 IllegalStateException: Optional int parameter 'num' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the cor
  GET /x1/12 → 200 x1 id=12
  GET /x1/abc → 400 MethodArgumentTypeMismatchException: Method parameter 'id': Failed to convert value of type 'java.lang.String' to required type 'int'; For input string: "abc"

重复参数和逗号分隔都能进 List;不带注解的 int 缺失是 500。

请求头与 Servlet 类型

实测 · 本机执行输出
  GET /h1 → 400 MissingRequestHeaderException: Required request header 'X-Id' for method parameter type String is not present
  GET /h1 → 200 h1 id=7,sid=abc
  GET /x2 → 200 x2 method=GET,locale=zh_CN,principal=null

@RequestHeader、@CookieValue 和 Servlet 相关的类型可以直接作为参数。

@RequestBody

实测 · 本机执行输出
  POST /b1  [{"name":"ann","age":30}] → 200 b1 User[name=ann, age=30]
  POST /b1  [{"name":"ann","age":"x"}] → 400 HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `int` from String "x": not a valid `int` value
  POST /b1  [{bad json] → 400 HttpMessageNotReadableException: JSON parse error: Unexpected character ('b' (code 98)): was expecting double-quote to start property name
  POST /b1 → 400 HttpMessageNotReadableException: Required request body is missing: java.lang.String demo.Main.b1(User)
  POST /b1  [{"name":"ann","age":30,"extra":1}] → 200 b1 User[name=ann, age=30]
  POST /b2 → 200 b2 null
  POST /b3?q=z  [raw text] → 200 b3 raw=raw text,q=z

JSON 错误、空 body 都是 400,未知字段默认被忽略。

对象绑定

实测 · 本机执行输出
  POST /m1  [name=ann&age=30&tags=a&tags=b] → 200 m1 Form(name=ann,age=30,tags=[a, b])
  POST /m1?name=q&age=9 → 200 m1 Form(name=q,age=9,tags=null)
  POST /m1  [name=ann&age=x] → 400 MethodArgumentNotValidException: Validation failed for argument [0] in java.lang.String demo.Main.m1(Form): [Field error in object 'form' on field 'age': rejected value [x]; codes [typeMismatch.form.a
  POST /m1  [name=ann&nope=1] → 200 m1 Form(name=ann,age=0,tags=null)
  POST /m3  [name=ann&age=30] → 200 m3 FormPub(name=null,age=0)
  POST /m2?extra=e  [name=bob&age=1] → 200 m2 Form(name=bob,age=1,tags=null),extra=e

需要 setter;只有 public 字段的对象会静默绑不上。

分步演示

参数解析
本步的对象

    本文用到的工具与边界

    • 本机 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 的 SpringApplication(spring-boot-starter-webmvc,内嵌 Tomcat 11),JSON 用 Boot 4 默认的 Jackson;异常文本由继承 ResponseEntityExceptionHandler 的 @RestControllerAdvice 输出(不改状态码);开发时我曾写了一个捕获所有异常并返回 500 的通用处理器,它把类型转换失败也变成了 500,与 Spring 默认的 400 不同,这里的结果是改成标准处理器之后的
    • 没有验证:@Valid 校验(见后面的数据校验篇);multipart 文件参数(见文件上传篇);@RequestPart;自定义 HandlerMethodArgumentResolver 的优先级(见 DispatcherServlet 篇里的一个例子);Jackson 对未知字段的默认值在 Boot 以外环境下的不同;@ModelAttribute 的 allowedFields 与数据绑定安全
    京ICP备2026067502号-1京公网安备11010502063487号