Spring Boot请求参数校验问题:@NotNull注解未生效
问题:@NotNull注解在Spring Boot请求参数校验中未生效
我尝试为REST端点的请求参数添加校验,发现@NotBlank注解可以正常触发ConstraintViolationException异常,但@NotNull注解未生效。
代码示例
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import org.springframework.http.ResponseEntity; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/test") @Validated class MyController{ @GetMapping(value = "/data", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Data> getData( @RequestParam @NotNull(message = "age is required") Integer age, @RequestParam @NotBlank(message = "name is required") String name){ //do something } }
使用依赖
implementation 'org.springframework.boot:spring-boot-starter-validation'
测试情况(Postman)
- http://localhost:8080/myApp/test/data?age=&name='myname' - 未抛出
ConstraintViolationException - http://localhost:8080/myApp/test/data?age=null&name='myname' - 未抛出
ConstraintViolationException - http://localhost:8080/myApp/test/data?name='myname' - 未抛出
ConstraintViolationException - http://localhost:8080/myApp/test/data?age=22&name='' - 抛出
ConstraintViolationException
使用环境
Java 17、Spring Boot 2.7.7
请问我可能遗漏了什么?
解答
这是Spring处理@RequestParam参数的类型转换逻辑与JSR-380校验注解的触发条件不匹配导致的,具体原因和解决方法如下:
1. 核心问题拆解
- 不传age参数:
@RequestParam默认required=false,Spring会直接将age赋值为null,但此时不会触发@NotNull校验——因为@Validated触发的校验是在参数绑定完成后执行,而Spring默认允许非必填参数为null。 - 传age=:空字符串转换
Integer会直接抛出TypeMismatchException,这个异常属于Spring的类型转换异常,会被全局异常处理器优先捕获,根本走不到@NotNull的校验流程。 - 传age=null:URL中的
null是字符串值,转换Integer同样会抛出TypeMismatchException,同样无法触发@NotNull校验。
2. 解决方法
方法一:给@RequestParam添加required=true
结合@NotNull使用,既强制参数必须传递,又校验参数不能为null:
@RequestParam(required = true) @NotNull(message = "age is required") Integer age
此时不传age会先抛出MissingServletRequestParameterException,若参数传递后为合法的null值(如通过其他绑定方式传入),则会触发@NotNull的校验异常。
方法二:处理类型转换异常
自定义全局异常处理器,捕获TypeMismatchException,针对age这类数值型参数,抛出类似校验失败的提示,统一异常返回格式:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(TypeMismatchException.class) public ResponseEntity<String> handleTypeMismatch(TypeMismatchException e) { String paramName = ((ServletRequestBindingException) e).getParameterName(); if ("age".equals(paramName)) { return ResponseEntity.badRequest().body("age is required and must be a valid integer"); } return ResponseEntity.badRequest().body("Invalid parameter type"); } }
补充:注解适用场景区分
@NotNull:用于非字符串类型(Integer、Long等),仅校验对象不能为null,不处理类型转换异常。@NotBlank:仅用于字符串,校验字符串不能为null且去除首尾空格后长度大于0,刚好适配字符串空值的场景。
内容的提问来源于stack exchange,提问作者har123
相关产品推荐
相关产品推荐

