Spring Validation:嵌套对象中BigDecimal的@NotNull校验失效求助
解决嵌套记录类的Spring Boot接口校验问题
问题根源分析
你的注解配置本身逻辑正确,但未生效通常有两个原因:
- 缺少校验依赖,导致Bean Validation未启用
- 未处理类型转换异常(比如
price传字符串时Jackson无法解析为Price对象)和校验异常的全局捕获,无法将异常映射为BAD_REQUEST响应
解决方案步骤
1. 确保引入校验依赖
如果使用Maven,在pom.xml中添加Spring Boot校验 starter:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
Gradle用户则添加:
implementation 'org.springframework.boot:spring-boot-starter-validation'
2. 保留现有注解配置(无需修改)
你的记录类注解已经覆盖了price为null或空请求的场景:
@PostMapping(path = "/books", produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity<Void> createBook(@Valid @RequestBody CreateBookRequest request); public record CreateBookRequest( @Valid @NotNull(message = "price不能为空") Price price ) {} public record Price( @NotNull(message = "price.value不能为空") BigDecimal value ) {}
3. 添加全局异常处理器
通过@RestControllerAdvice捕获两类关键异常,统一返回BAD_REQUEST响应,也可抛出自定义异常:
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 org.springframework.http.converter.HttpMessageNotReadableException; import java.util.List; import java.util.stream.Collectors; @RestControllerAdvice public class GlobalValidationExceptionHandler { // 处理Bean Validation校验失败(比如price为null) @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleValidationErrors(MethodArgumentNotValidException ex) { List<String> errorMessages = ex.getBindingResult() .getFieldErrors() .stream() .map(FieldError::getDefaultMessage) .collect(Collectors.toList()); return new ResponseEntity<>(new ErrorResponse("VALIDATION_FAILED", errorMessages), HttpStatus.BAD_REQUEST); } // 处理请求格式错误(比如price传字符串"",Jackson无法解析为Price对象) @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity<ErrorResponse> handleFormatErrors(HttpMessageNotReadableException ex) { String message = "请求格式非法:price必须是包含value的对象,不能为null、空字符串或其他类型"; return new ResponseEntity<>(new ErrorResponse("INVALID_REQUEST_FORMAT", List.of(message)), HttpStatus.BAD_REQUEST); } // 自定义异常处理(可选,若需要抛自定义异常) @ExceptionHandler(CustomBookValidationException.class) public ResponseEntity<ErrorResponse> handleCustomValidation(CustomBookValidationException ex) { return new ResponseEntity<>(new ErrorResponse("CUSTOM_VALIDATION_ERROR", List.of(ex.getMessage())), HttpStatus.BAD_REQUEST); } // 统一错误响应结构 public record ErrorResponse(String code, List<String> messages) {} } // 自定义异常类(可选) class CustomBookValidationException extends RuntimeException { public CustomBookValidationException(String message) { super(message); } }
各无效场景的处理效果
| 无效请求JSON | 处理逻辑 |
|---|---|
{"price": null} | 触发@NotNull校验,抛出MethodArgumentNotValidException,返回BAD_REQUEST |
{"price": ""} | Jackson无法将字符串转为Price对象,抛出HttpMessageNotReadableException,返回BAD_REQUEST |
{} | price字段未传值,视为null,触发@NotNull校验,返回BAD_REQUEST |
内容的提问来源于stack exchange,提问作者bmrki
相关产品推荐
相关产品推荐

