数据校验:@Valid/@Validated 与 BindingResult/Errors

接口收参数不能「来者不拒」。Spring MVC 直接接入了 Jakarta Bean Validation 规范:在 DTO 字段上写约束注解,方法参数上加 @Valid 或 @Validated 触发校验,框架自动执行并反馈错误。

前置依赖

Spring 6 使用 Jakarta 命名空间,需要 Bean Validation 实现(如 Hibernate Validator):

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>8.0.1.Final</version>
</dependency>

第一步:在 DTO 上写约束

import jakarta.validation.constraints.*;

public class UserDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "长度需在 2~20")
    private String name;
    @NotNull(message = "年龄不能为空")
    @Min(0) @Max(150)
    private Integer age;
    @Email
    private String email;
}

第二步:在参数上触发校验

@RestController
public class UserApi {
    @PostMapping("/api/users")
    public User create(@Valid @RequestBody UserDTO dto) {  // 校验失败抛 MethodArgumentNotValidException
        return userService.create(dto);
    }
}
  • @RequestBody 校验失败抛 MethodArgumentNotValidException(默认映射 400,message 汇总在异常里);也可紧跟一个 BindingResult/Errors 参数接住错误自行处理(不再抛异常):
@PostMapping("/api/users")
public String create(@Valid @RequestBody UserDTO dto, BindingResult result) {
    if (result.hasErrors()) {
        return result.getFieldErrors().stream()
                .map(e -> e.getField() + ":" + e.getDefaultMessage())
                .toList().toString();
    }
    return "ok";
}
// 输出示例:[name:用户名不能为空]

@ModelAttribute / 表单绑定场景

非请求体(表单提交、查询参数拼对象)同样可校验,BindingResult 必须紧跟被校验的参数之后,否则框架直接报错:

@PostMapping("/user/save")
public String save(@Valid UserDTO dto, BindingResult result) { // 顺序不能换
    return result.hasErrors() ? "校验失败" : "成功";
}

@Valid 与 @Validated

注解来源区别
@Validjakarta.validation.ValidJSR 标准,可用在方法参数/属性上做级联校验
@Validatedorg.springframework.validation.annotation.ValidatedSpring 扩展,主要支持校验分组

分组校验示例:新增时不校验 id,修改时校验 id。

public interface UpdateGroup {}
// 使用分组:@Validated(UpdateGroup.class) @RequestBody UserDTO dto
public class UserDTO {
    @Null(groups = CreateGroup.class)
    @NotNull(groups = UpdateGroup.class)
    private Long id;
}

方法级校验(校验方法入参/返回值而非 DTO 字段)需在类上标 @Validated 并配合方法校验器,配置细节以官方文档为准。

错误消息的国际化

约束注解的 message 默认在 ValidationMessages.properties 中按 key 查找,直接写死中文也能用;想配合 MessageSource 统一管理,需借助 LocalValidatorFactoryBean 桥接,方案以官方文档为准。

常见坑

  • 只加约束注解不触发:校验注解写在方法参数的对象上并加 @Valid/@Validated 才生效,类上加注解不会自动校验每个方法。
  • 忘了约束空字符串:String 字段用 @NotBlank 而不是 @NotNull(后者放行 "")。
  • BindingResult 没紧跟被校验参数:抛 IllegalStateException。

校验 = 约束注解(声明规则)+ @Valid/@Validated(触发点)+ BindingResult/异常(接收结果)。三层配合,脏数据在进 Service 之前就被拦下。

笔记加载中…