校验枚举类型

1.介绍

使用自定义注解校验枚举类型

2.校验枚举

大多数标准注解都不支持枚举的校验。

例如

当将 @Pattern 注解应校验枚举时, Hibernate Validator会报以下错误:

javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint ‘javax.validation.constraints.Pattern’ validating type ‘com.niugang.javabase.enums.CustomerTypeEnum’. Check configuration for ‘customerTypeMatchesPattern’

实际上,可以应用于枚举的唯一标准注解是@NotNull 和@Null。

3.校验枚举的类型

准备

public enum  CustomerTypeEnum {
    
    
    /**
     *
     */
    NEW,
    OLD,
    EDIT,
    DEFAULT;
}

定义一个注解来校验枚举的类型:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class EnumNamePatternValidator implements ConstraintValidator<EnumNamePattern, Enum<?>> {
    
    

    private Pattern pattern;

    @Override
    public void initialize(EnumNamePattern annotation) {
    
    
        try {
    
    
            pattern = Pattern.compile(annotation.regexp());
        } catch (PatternSyntaxException e) {
    
    
            throw new IllegalArgumentException("错误的正则", e);
        }
    }

    @Override
    public boolean isValid(Enum<?> value, ConstraintValidatorContext context) {
    
    
        if (value == null) {
    
    
            return true;
        }

        Matcher m = pattern.matcher(value.name());
        return m.matches();
    }
}

实现与标准的@Pattern 验证器非常相似。 但是,这一次,匹配了枚举的名称。

验证Bean

@Data
public class UserDto {
    
    
    @NotNull(message = "姓名不能为空")
    private String name;

    @AssertTrue
    private boolean working;

    @Size(min = 10, max = 200, message
            = "关于我在10到200字符")
    private String aboutMe;

    @Min(value = 18, message = "年龄最小18岁")
    @Max(value = 150, message = "年龄最大150岁")
    private int age;

    @Email(message = "邮箱无效")
    private String email;
    @EnumNamePattern(regexp = "NEW|DEFAULT")
    private CustomerTypeEnum customerType;
}

测试

  UserDto user = new UserDto();
        user.setName("张三");
        user.setWorking(true);
        user.setAboutMe("来自北京的张三,住在朝阳区");
        user.setAge(50);
        user.setCustomerType(CustomerTypeEnum.EDIT);
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<UserDto>> violations = validator.validate(user);
        for (ConstraintViolation<UserDto> violation : violations) {
    
    
            System.out.println(violation);

        }

ConstraintViolationImpl{interpolatedMessage=‘请匹配 “NEW|DEFAULT”’, propertyPath=customerType, rootBeanClass=class com.niugang.javabase.pojo.UserDto, messageTemplate=‘请匹配 “{regexp}”’}

4.校验枚举的子集

将枚举与正则表达式匹配不是类型安全的。 相反,与枚举的实际值进行比较更有意义。

但是,由于注解的限制,这样的注解不能通用。 这是因为注解的参数只能是特定枚举的具体值,而不是枚举父类的实例。

为 CustomerTypeEnum 枚举创建特定的子集校验注解:

@Target({
    
    METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = CustomerTypeSubSetValidator.class)
public @interface CustomerTypeSubset {
    
    
    CustomerTypeEnum[] anyOf();

    String message() default "必须为任何一个 {anyOf}";

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

    Class<? extends Payload>[] payload() default {
    
    };
}
@CustomerTypeSubset(anyOf = {
    
    CustomerTypeEnum.NEW, CustomerTypeEnum.OLD})
private CustomerTypeEnum customerType;

需要定义 CustomerTypeSubSetValidator 来检查给定枚举值的列表是否包含当前值:

public class CustomerTypeSubSetValidator implements ConstraintValidator<CustomerTypeSubset, CustomerTypeEnum> {
    
    
    private CustomerTypeEnum[] subset;

    @Override
    public void initialize(CustomerTypeSubset constraint) {
    
    
        this.subset = constraint.anyOf();
    }

    @Override
    public boolean isValid(CustomerTypeEnum value, ConstraintValidatorContext context) {
    
    
        return value == null || Arrays.asList(subset).contains(value);
    }
}

5.验证字符串是否匹配枚举的值

创建一个注解来检查字符串对于特定枚举是否有效。

@Target({
    
    METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = ValueOfEnumValidator.class)
public @interface ValueOfEnum {
    
    
    Class<? extends Enum<?>> enumClass();

    String message() default "必须为 {enumClass},中的枚举值";

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

    Class<? extends Payload>[] payload() default {
    
    };
}

校验逻辑

public class ValueOfEnumValidator implements ConstraintValidator<ValueOfEnum, CharSequence> {
    
    
    private List<String> acceptedValues;

    @Override
    public void initialize(ValueOfEnum annotation) {
    
    
        acceptedValues = Stream.of(annotation.enumClass().getEnumConstants())
                .map(Enum::name)
                .collect(Collectors.toList());
    }

    @Override
    public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
    
    
        if (value == null) {
    
    
            return true;
        }

        return acceptedValues.contains(value.toString());
    }
}

@ValueOfEnum(enumClass = CustomerTypeEnum.class)
private String customerTypeString;

这种验证在处理 JSON 对象时特别有用。 因为出现了下面的异常,当把不正确的值从 JSON 对象映射到枚举时:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/niugang0920/article/details/119698816