Sam's Notes | Sam Blog

梦想还是要有的,万一实现了呢

0%

spring Validator

主要内容

JSR-303 Validation
JSR-303 是 JAVA EE 中的一项子规范,叫做 Bean Validation,官方参考实现是Hibernate Validator。

本文以 spring boot V2.4.6 版本为例说明如何使用。

更新历史


JSR 303 基本的校验规则

常用的

  • 空检查
    @Null 验证对象是否为null
    @NotNull 验证对象是否不为null, 无法查检长度为0的字符串
    @NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
    @NotEmpty 检查约束元素是否为NULL或者是EMPTY.

  • Booelan检查
    @AssertTrue 验证 Boolean 对象是否为 true
    @AssertFalse 验证 Boolean 对象是否为 false

  • 长度检查
    @Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
    @Length(min=, max=) 验证约束字符串是否包含在最小和最大之间。

  • 日期检查
    @Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
    @Future 验证 Date 和 Calendar 对象是否在当前时间之后,验证成立的话被注释的元素一定是一个将来的日期
    @Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式,regexp:正则表达式 flags: 指定Pattern.Flag 的数组,表示正则表达式的相关选项。

  • 数值检查
    建议使用在Stirng,Integer类型,不建议使用在 int 类型上,因为表单值为 空 时无法转换为 int,
    @Min 验证 Number 和 String 对象是否大等于指定的值
    @Max 验证 Number 和 String 对象是否小等于指定的值
    @DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度( Double, float, BigDecimal )
    @DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
    @Digits 验证 Number 和 String 的构成是否合法
    @Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
    @Range(min=, max=) 被指定的元素必须在合适的范围内
    @Range(min=10000,max=50000,message=”range.bean.wage”)
    @Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
    @CreditCardNumber 信用卡验证
    @Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
    @ScriptAssert(lang= ,script=, alias=)
    @URL(protocol=,host=, port=,regexp=, flags=)

spring 结合

自动触发

  • controller
    在 controller 中搭配 @Valid BindingResult 使用, 校验entity的数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @PostMapping(value = "app")
    public ResponseEntity add(@Valid @ModelAttribute AppEntity entity , BindingResult result) {
    if (result.hasErrors()) {
    return ResponseEntity.badRequest().body(result.getAllErrors().stream()
    .map(DefaultMessageSourceResolvable::getDefaultMessage)
    .reduce((m1, m2) -> m1 + ";" + m2)
    .orElse("参数输入有误!"));
    }
    }
  • Spring data jpa
    Spring data jpa 在事务提交前也会触发,如果数据没通过校验会出现如下类似的异常

    1
    2
    3
    4
    5
    6
    7
    8
    org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
    ...

    Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [xxx.xxx.xxxEntity] during update time for groups [javax.validation.groups.Default, ]
    List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='不是金额形式', propertyPath=mobile, rootBeanClass=class xxx.xxx.xxxEntity, messageTemplate='不是金额形式'}
    ConstraintViolationImpl{interpolatedMessage='个数必须在0和16之间', propertyPath=mobile, rootBeanClass=class xxx.xxx.xxxEntity, messageTemplate='{javax.validation.constraints.Size.message}'}
    ...

    这是异常,应该要避免出现。

手动触发

Bean Validation 还可自定义规划并且手动触发校验。
注意 MoneyValidatorMoney 相互一致

  • 自定义Validation注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import xxx.MoneyValidator;

    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy= MoneyValidator.class)
    public @interface Money {
    String message() default"不是金额形式";

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

    Class<? extends Payload>[] payload() default {};
    }
  • 自定义Validation校验

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import xxx.Money;

    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import java.util.regex.Pattern;

    public class MoneyValidator implements ConstraintValidator<Money, String> {

    private String moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式
    private Pattern moneyPattern = Pattern.compile(moneyReg);

    @Override
    public void initialize(Money constraintAnnotation) {
    ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
    if (value == null)
    return true;
    return moneyPattern.matcher(value).matches();
    }
    }
  • 手动触发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import javax.validation.ConstraintViolation;
    import javax.validation.Validator;
    import javax.validation.groups.Default;
    import java.util.List;
    import java.util.Set;

    @SpringBootTest(classes = Application.class)
    public class ServiceTest{

    // 自动注入 validator
    @Autowired
    private Validator validator;


    @Test
    public void userTest() {
    UrUserEntity user = new UrUserEntity();

    user.setVersion(0);
    user.setMobile("123456789012345678s");

    // 手动触发
    Set<ConstraintViolation<UrUserEntity>> violationSet = validator.validate(user, Default.class);
    if (null != violationSet) {
    String messages = violationSet.stream()
    .map(ConstraintViolation::getMessage)
    .reduce((m1, m2) -> m1 + ";" + m2)
    .orElse("参数输入有误!");
    System.out.println(messages);
    }

    }
    }

  • bean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Entity
    public class UrUserEntity {

    @Column(name = "mobile", length = 16, nullable = false)
    @Size(max = 16)
    @Money
    private String mobile;

    ...
    }

    @Money 给 mobile校验仅仅是玩笑