Validation

beanvalidation是一种规范,hibernate-validator是它的最佳实现。

常用注解

beanvalidation 所有注解在 javax.validation.constraints 包下:

img

hibernate-validator 所有注解在 org.hibernate.validator.constraints 包下:

img

常用注解解释

约束注解 详细信息
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内

约束与校验器类的绑定原理

org.hibernate.validator.internal.metadata.core.ConstraintHelper 类下:

img

img

  • 从上述两幅图中可以看出,NotEmpty注解可以校验字符串,集合,Map,数组类型的属性,而为了满足一个注解可以校验多种类型的数据,所有需要为这个注解适配多个对应的校验器。
  • 注解名称是xxx,那么校验器的名称就是xxxValidator。

自定义消息模板

消息模板中可以使用 el 表达式。

public class User {
    @NotNull(groups = {Update.class})
    private Integer id;
    @NotBlank(message = "名字不能为空")
    private String name;
    @Min(value = 18, message = "年龄小于{value},不得进入")
    private int age;
}

分组校验

注解上不区分组的话,会使用默认的组:javax.validation.groups.Default

public class User {
    @NotNull(groups = {Update.class})
    private Integer id;
    @NotBlank(message = "名字不能为空")
    private String name;
    @Min(value = 18, message = "年龄小于{value},不得进入")
    private int age;

    public interface Insert {
    }

    public interface Update {
    }
}

特别注意的地方如果使用了某个组进行校验的话,那么默认的组就不会进行校验****。就比如校验 Update 组,那么就算 name 名字为空也不会再校验。如果想在对某个组进行校验,默认的组同时被校验的话,可以让 分组接口继承 Default 接口即可****。

public class User {
    @NotNull(groups = {Update.class})
    private Integer id;
    @NotBlank(message = "名字不能为空")
    private String name;
    @Min(value = 18, message = "年龄小于{value},不得进入")
    private int age;

    public interface Insert {
    }

    public interface Update extends Default {
    }
}

级联校验

public class Grade {
    @NotBlank
    private String no;
}
public class User {
    @NotNull(groups = {Update.class})
    private Integer id;
    @NotBlank(message = "名字不能为空")
    private String name;
    @Min(value = 18, message = "年龄小于{value},不得进入")
    private int age;
    @NotNull(message = "年级不能为空")
    @Valid
    private Grade grade;

    public interface Insert {
    }

    public interface Update extends Default {
    }
}

被引用对象加上 @valid 注解才能完成级联校验。也就是说在校验 User 对象里的属性的时候,能一起校验 Grade 对象。

自定义校验规则

public class User {
    @NotNull(groups = {Update.class})
    private Integer id;
    @NotBlank(message = "名字不能为空")
    private String name;
    @Min(value = 18, message = "年龄小于{value},不得进入")
    private int age;
    @NotNull(message = "年级不能为空")
    @Valid
    private Grade grade;
    @UserStatus
    private Integer userStatus;

    public interface Insert {
    }

    public interface Update extends Default {
    }
}
@Constraint(validatedBy = {UserStatusValidator.class})
@Target({FIELD})
@Retention(RUNTIME)
public @interface UserStatus {
    String message() default "用户状态不正确,值必须在1001或者1002或者1003中";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
public class UserStatusValidator implements ConstraintValidator<UserStatus, Integer> {
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        return Arrays.asList(1001, 1002, 1003).contains(value);
    }
}

当现有的约束注解不满足实际项目需要时,可以仿照现有的 NotEmpty 注解和 NotEmptyValidatorForArray 校验器进行改造以下,就可以实现自己的校验规则。

@Validated自动校验

在 springboot 中可以使用在控制器中给每个需要进行校验的方法的参数前加上 @Validated 注解,在参数校验不通过的时候会自动抛出 BindException 异常。

@PutMapping
public String update(@Validated({User.Update.class}) User user) {
    return "更新成功!";
}

统一异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = BindException.class)
    public RespBean entityException(BindException e) {
        String msg = e.getBindingResult().getFieldError().getDefaultMessage();
        return  RespBean.error(HttpStatus.BAD_REQUEST.value(), msg);
    }
}
posted @ 2021-10-13 19:10  喜欢小让  阅读(649)  评论(0编辑  收藏  举报