数据校验:@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
| 注解 | 来源 | 区别 |
|---|---|---|
| @Valid | jakarta.validation.Valid | JSR 标准,可用在方法参数/属性上做级联校验 |
| @Validated | org.springframework.validation.annotation.Validated | Spring 扩展,主要支持校验分组 |
分组校验示例:新增时不校验 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 之前就被拦下。