Spring Validation
字数
861 字
阅读时间
5 分钟
1 简介
Spring Validation 提供了便捷的数据校验功能,主要通过 @Valid
和 @Validated
两个注解来实现。这两个注解虽然功能类似,但使用场景和特点有所不同。
2 @Valid 注解
2.1 基本使用
@Valid
是 JSR-303 规范定义的注解,用于验证嵌套对象的属性:
java
public class OrderDTO {
@NotNull(message = "用户ID不能为空")
private Long userId;
@Valid // 对 address 对象进行验证
private Address address;
}
public class Address {
@NotEmpty(message = "街道不能为空")
private String street;
@Pattern(regexp = "^\\d{6}$", message = "邮编格式不正确")
private String zipCode;
}
@RestController
public class OrderController {
@PostMapping("/orders")
public void createOrder(@Valid @RequestBody OrderDTO orderDTO) {
// 如果验证失败,会抛出 MethodArgumentNotValidException
}
}
2.2 常见验证注解
@NotNull
: 不能为 null@NotEmpty
: 不能为空(字符串、集合、数组等)@NotBlank
: 不能为空白字符串@Size
: 限制大小@Pattern
: 正则表达式匹配@Email
: 邮箱格式@Max/@Min
: 最大/最小值@Range
: 范围值
3 @Validated 注解
3.1 分组验证
@Validated
是 Spring 提供的注解,扩展了 @Valid
的功能,支持分组验证:
java
public interface AddGroup {}
public interface UpdateGroup {}
public class UserDTO {
@Null(groups = AddGroup.class) // 新增时ID必须为空
@NotNull(groups = UpdateGroup.class) // 更新时ID不能为空
private Long id;
@NotBlank(groups = {AddGroup.class, UpdateGroup.class}) // 新增和更新时都不能为空
private String name;
}
@RestController
public class UserController {
@PostMapping("/users")
public void addUser(@Validated(AddGroup.class) @RequestBody UserDTO userDTO) {
// 只验证 AddGroup 组的约束
}
@PutMapping("/users")
public void updateUser(@Validated(UpdateGroup.class) @RequestBody UserDTO userDTO) {
// 只验证 UpdateGroup 组的约束
}
}
3.2 方法级别验证
@Validated
还可以用于类级别,实现方法参数和返回值的验证:
java
@Service
@Validated
public class UserService {
public void updateUser(@NotNull(message = "用户ID不能为空") Long id,
@Valid UserDTO userDTO) {
// 方法参数验证
}
@NotNull(message = "返回用户信息不能为空")
public UserDTO getUser(Long id) {
// 返回值验证
return userDTO;
}
}
4 统一异常处理
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex) {
Map<String, String> errors = new HashMap<>();
ex.getConstraintViolations().forEach(violation ->
errors.put(violation.getPropertyPath().toString(), violation.getMessage())
);
return ResponseEntity.badRequest().body(errors);
}
}
5 最佳实践
使用场景选择:
- 使用
@Valid
用于嵌套对象的验证 - 使用
@Validated
用于分组验证和方法级别的验证
- 使用
自定义验证:
java
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneValidator implements ConstraintValidator<Phone, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return value.matches("^1[3-9]\\d{9}$");
}
}
- 验证分组顺序:
java
@GroupSequence({AddGroup.class, UpdateGroup.class})
public interface GroupOrder {}
@PostMapping("/users")
public void addUser(@Validated(GroupOrder.class) @RequestBody UserDTO userDTO) {
// 按顺序验证 AddGroup 和 UpdateGroup
}
6 注意事项
@Valid
和@Validated
的主要区别:@Valid
是 JSR-303 规范的标准注解@Validated
是 Spring 的扩展注解@Valid
不支持分组验证@Validated
支持分组验证和方法级别验证
性能考虑:
- 验证注解会在运行时进行反射,建议只在必要的字段上添加验证
- 对于大量重复验证的场景,考虑使用缓存
安全性:
- 不要在验证消息中包含敏感信息
- 考虑对验证失败的日志进行脱敏处理