跳转至

SpringMVC 参数校验实现方案

一、JSR-303 / JSR-380

Java 官方校验规范:

  • JSR-303(Bean Validation 1.0):@NotNull@Size@Min@Max
  • JSR-380(Bean Validation 2.0):加 @Email@NotBlank@Positive@NegativeOrZero

Hibernate Validator 是参考实现,Spring Boot 自带 starter。

二、常用注解

注解 作用
@NotNull 不能为 null("" 可以)
@NotBlank 字符串不能为 null 且 trim 后非空
@NotEmpty 不能为 null 且 size > 0(字符串、集合)
@Size(min, max) 长度范围
@Min / @Max 数值范围
@Email 邮箱格式
@Pattern(regexp) 正则
@Positive / @PositiveOrZero 正数
@Past / @Future 过去 / 未来日期

三、Controller 用法

1. 表单 / Query 参数

@PostMapping("/user")
public String create(@Validated @RequestBody UserDTO dto,
                     BindingResult result) {
    if (result.hasErrors()) {
        return result.getFieldError().getDefaultMessage();
    }
    userService.create(dto);
    return "ok";
}

DTO:

public class UserDTO {
    @NotBlank(message = "姓名不能为空")
    @Size(max = 20, message = "姓名最长 20")
    private String name;

    @NotNull(message = "年龄不能为空")
    @Min(value = 0, message = "年龄不能为负")
    private Integer age;

    @Email(message = "邮箱格式不正确")
    private String email;
}

BindingResult 必须紧跟在 @Valid 对象后面

否则 Spring 直接抛 MethodArgumentNotValidException,不会进入你的方法。

2. 路径参数 / Query 参数

@GetMapping("/user/{id}")
public User get(@PathVariable @Min(1) Long id) { ... }

类上要加 @Validated

四、全局异常处理

不手动写 if (result.hasErrors()),统一由 @RestControllerAdvice 处理:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleValid(MethodArgumentNotValidException e) {
        String msg = e.getBindingResult().getFieldError().getDefaultMessage();
        return Result.fail(400, msg);
    }

    @ExceptionHandler(BindException.class)
    public Result handleBind(BindException e) { ... }

    @ExceptionHandler(ConstraintViolationException.class)
    public Result handleConstraint(ConstraintViolationException e) { ... }
}

五、分组校验

不同场景校验规则不同:

public interface Create {}
public interface Update {}

public class UserDTO {
    @NotNull(groups = Update.class)
    private Long id;

    @NotBlank(groups = {Create.class, Update.class})
    private String name;
}

@PostMapping("/user")
public String create(@Validated(Create.class) @RequestBody UserDTO dto) { ... }

六、自定义校验注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdCardValidator.class)
public @interface IdCard {
    String message() default "身份证号格式错误";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class IdCardValidator
        implements ConstraintValidator<IdCard, String> {
    public boolean isValid(String value, ConstraintValidatorContext ctx) {
        return value != null && value.matches("^\\d{17}[\\dXx]$");
    }
}

高频追问

  • @Validated@Valid:前者是 Spring 提供,支持分组;后者是 JSR 标准,不支持分组。
  • @RequestBody 校验失败抛 MethodArgumentNotValidException;表单对象校验失败抛 BindException;方法参数级校验失败抛 ConstraintViolationException