You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Validation:嵌套对象中BigDecimal的@NotNull校验失效求助

解决嵌套记录类的Spring Boot接口校验问题

问题根源分析

你的注解配置本身逻辑正确,但未生效通常有两个原因:

  1. 缺少校验依赖,导致Bean Validation未启用
  2. 未处理类型转换异常(比如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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 16:01:01