Spring中含嵌套异构对象的JSON请求体的BindingResult验证问题
在Spring控制器中验证嵌套对象与列表的全字段非空
嘿,这个场景我太熟悉了!要实现所有字段(包括嵌套对象和列表里的元素)的非空验证,咱们分三步走就行,全程用Spring自带的验证机制搞定:
1. 给实体类添加验证注解
首先得给每个实体的字段都加上对应的验证注解,还要记得开启嵌套验证:
子实体类(Person、MovieList)
给每个必填字段加上合适的注解:
- 字符串类型用
@NotBlank(确保非空且不是空白字符) - 数值类型用
@NotNull(确保不为null)
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; public class Person { @NotBlank(message = "姓名不能为空") private String name; @NotNull(message = "年龄不能为空") private Integer age; // 构造器、getter/setter省略 } public class MovieList { // 建议改名成Movie更贴合语义哦 @NotBlank(message = "电影名称不能为空") private String name; @NotNull(message = "时长不能为空") private Integer duration; @NotBlank(message = "语言不能为空") private String language; // 构造器、getter/setter省略 }
父实体类(Customer)
除了给嵌套字段加@NotNull,还要加上@Valid注解——这个是关键!它会告诉Spring递归验证嵌套的对象,包括列表里的每一个元素:
import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.util.List; public class Customer { @NotNull(message = "person对象不能为空") @Valid private Person person; @NotNull(message = "电影列表不能为空") @Valid private List<MovieList> movieList; // 构造器、getter/setter省略 }
2. 控制器中启用验证
在接收请求的控制器方法里,给@RequestBody参数加上@Valid(或者@Validated,后者支持分组验证),同时可以绑定BindingResult来手动处理验证错误:
import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import jakarta.validation.Valid; @RestController public class CustomerController { @PostMapping("/customer") public ResponseEntity<String> createCustomer( @Valid @RequestBody Customer customer, BindingResult bindingResult) { // 检查是否有验证错误 if (bindingResult.hasErrors()) { StringBuilder errorMessage = new StringBuilder(); bindingResult.getAllErrors().forEach(error -> errorMessage.append(error.getDefaultMessage()).append("; ") ); return ResponseEntity.badRequest().body(errorMessage.toString()); } // 验证通过,执行业务逻辑 return ResponseEntity.ok("所有字段验证通过,请求已接收"); } }
3. 确保依赖到位
别忘了在你的pom.xml(Maven)或build.gradle(Gradle)里引入Spring验证的 starter 依赖:
Maven 依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
Gradle 依赖
implementation 'org.springframework.boot:spring-boot-starter-validation'
进阶:全局异常处理(更优雅的方式)
如果不想每个控制器方法都写BindingResult的处理逻辑,可以用全局异常处理器统一捕获MethodArgumentNotValidException,返回标准化的错误响应:
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.HashMap; import java.util.Map; @RestControllerAdvice public class GlobalValidationExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) { Map<String, String> errors = new HashMap<>(); ex.getBindingResult().getAllErrors().forEach(error -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage); }); return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); } }
用这种方式的话,控制器方法里就可以去掉BindingResult,代码更简洁:
@PostMapping("/customer") public ResponseEntity<String> createCustomer(@Valid @RequestBody Customer customer) { // 执行业务逻辑 return ResponseEntity.ok("所有字段验证通过,请求已接收"); }
这样一来,不管是嵌套的Person对象,还是列表里的每个MovieList对象,所有字段的非空验证都会被Spring自动执行,完美覆盖你的需求!
内容的提问来源于stack exchange,提问作者Prash
相关产品推荐
相关产品推荐

