SpringBoot项目自定义校验枚举注解

SpringBoot项目自定义校验枚举注解,第1张

SpringBoot项目自定义校验枚举注解

文章目录

1. 使用场景2. 技术实现

2.1 实现思路2.2 代码实现

2.2.1 引入依赖2.2.2 自定义校验注解2.2.3 校验工具类2.2.4 校验处理逻辑 3. 使用示例

1. 使用场景

某接口的入参某属性,只希望为系统中某枚举的中定义过的值。


例如:用户类型枚举中定义 10-普通用户 20-管理员 30-游客 40-中级用户 50-高级用户
希望某接口的入参 userType 只能填入这几种type


支持扩展场景:

    只允许填入枚举中定义过的某些值不允许填入某些值
2. 技术实现 2.1 实现思路

使用Hibernate Validator校验工具,自定义校验注解及其校验逻辑。


Hibernate Validator官方文档:
https://docs.jboss.org/hibernate/validator/7.0/reference/en-US/html_single/#validator-gettingstarted

2.2 代码实现 2.2.1 引入依赖
 
     org.springframework.boot
     spring-boot-starter-validation
 
 
2.2.2 自定义校验注解
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RUNTIME)
@Repeatable(VerifyEnum.List.class)
@Constraint(validatedBy = {EnumValidator.class})
public @interface VerifyEnum {

    
    Class> enumClass();


    
    String keyColumn();

    
    String[] allowedValues() default { };

    
    String[] notAllowedValues() default { };

    
    String message() default "";

    Class[] groups() default { };

    Class[] payload() default {};

    @Target({ElementType.PARAMETER, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface List {
        VerifyEnum[] value();
    }
}
2.2.3 校验工具类
public class ValidatorUtils {
    
    private static ValidatorFactory factory;
    
    public static Validator validator;
    
    public static Executablevalidator executablevalidator;

    static {
        initValidator();
        clean();
    }

    
    public static void initValidator() {
        factory = Validation.byProvider(Hibernatevalidator.class)
                .configure()
                .failFast(true)
                .buildValidatorFactory();
        validator = factory.getValidator();
    }

    
    public static void initExecutablevalidator() {
        factory = Validation.buildDefaultValidatorFactory();
        executablevalidator = factory.getValidator().forExecutables();
    }

    
    public static void clean() {
        factory.close();
    }

    
    public static Set> validMethod(T t, Method method, Object[] parameterValues){
        return executablevalidator.validateParameters(t, method, parameterValues);
    }

    
    public static void validateObject(Object object, Class... groups) {
        Set> constraintViolations = validator.validate(object, groups);
        if(CollectionUtil.isNotEmpty(constraintViolations)){
            String errorMsg = StrUtil.format("对象{}校验异常:{}", object.getClass().getSimpleName(), getErrorMsg(constraintViolations));
            throw new ApiException(ResultEnum.PARAMETER_VERIFICATION_FAIL.getCode(), errorMsg);
        }

    }

    
    public static void validateObjectList(List objectList, Class... groups) {
        for (Object o : objectList) {
            validateObject(o, groups);
        }
    }

    
    public static String validatevalues(String[]  allowedValues, String[] notAllowedValues, Object value) {
        // notAllowedValues存在情况
        if (notAllowedValues != null && notAllowedValues.length > 0) {
            List notAllowedList = Arrays.asList(notAllowedValues);
            if (notAllowedList.contains(String.valueOf(value))) {
                return StrUtil.format("不能填写以下值{}", notAllowedList);
            }
        }

        // allowedValues存在情况
        if (allowedValues != null && allowedValues.length > 0) {
            List allowedList = Arrays.asList(allowedValues);
            if (!allowedList.contains(String.valueOf(value))) {
                return StrUtil.format("可填值只能为{}", Arrays.toString(allowedValues));
            }
        }
        return "";
    }

    
    public static String validatevalues(String[]  allowedValues, String[] notAllowedValues,
                                        Object value, List codevalues) {
        // notAllowedValues存在情况
        if(notAllowedValues != null && notAllowedValues.length > 0){
            List notAllowedList = Arrays.asList(notAllowedValues);
            codevalues.removeAll(notAllowedList);
            if(notAllowedList.contains(String.valueOf(value))){
                return StrUtil.format("不能填写以下值{}", notAllowedList);
            }
        }

        // allowedValues存在情况
        if(allowedValues != null && allowedValues.length > 0){
            List allowedList = Arrays.asList(allowedValues);
            // 将codevalues统一转成String
            List stringCodevalues = codevalues.stream().map(String::valueOf).collect(Collectors.toList());
            if(!stringCodevalues.containsAll(allowedList)){
                // @VerifyEnum填入allowedValues存在非枚举code值
                throw new RuntimeException("填入allowedValues存在非允许值");
            }else{
                if(allowedList.contains(String.valueOf(value))){
                    return "";
                }else{
                    return StrUtil.format("可填值只能为{}", Arrays.toString(allowedValues));
                }
            }
        }

        // 校验字段值是否是字典允许数据
        if(codevalues.contains(value)){
            return "";
        }else{
            // 重置错误提示
            return StrUtil.format("可填值只能为{}", codevalues);
        }
    }

    
    public static String getErrorMsg(Set> c){
        StringBuffer msg = new StringBuffer();
        if (CollectionUtil.isNotEmpty(c)){
            for (ConstraintViolation constraintViolation : c) {
                String itemMessage = constraintViolation.getMessage();
                String itemName = constraintViolation.getPropertyPath().toString();
                msg.append("字段<").append(itemName).append(">--").append(itemMessage).append("。");
            }
        }
        return msg.toString();
    }

    
    public static void getOneInfo(StringBuffer s, Set> c){
        if (CollectionUtil.isNotEmpty(c)){
            s.append("{ ");
            for (ConstraintViolation constraintViolation : c) {
                String itemMessage = constraintViolation.getMessage();
                String itemName = constraintViolation.getPropertyPath().toString();
                s.append("字段<" + itemName + "> :" + itemMessage + "。");
            }
            s.append(" }");
        }
    }

    
    public static void getListInfo(StringBuffer s, List>> collect){
        for (int i = 0; i < collect.size(); i++) {
            Set> constraintViolations = collect.get(i);
            if (CollectionUtil.isNotEmpty(constraintViolations)){
                s.append("[ " + "列表第["+ i + "]项校验不通过:");
                getOneInfo(s, constraintViolations);
                s.append(" ]");
            }
        }
    }

    
    public static boolean getResult(String errorMsg, ConstraintValidatorContext context){
        if(StrUtil.isNotBlank(errorMsg)){
            // 重置错误提示
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(errorMsg)
                    .addConstraintViolation();
            return false;
        }
        return true;
    }
}
 
2.2.4 校验处理逻辑 
public class EnumValidator implements ConstraintValidator {

    private Class> enumClass;

    private String[]  allowedValues;

    private String[]  notAllowedValues;

    private String keyColumn;


    @Override
    public void initialize(VerifyEnum constraintAnnotation) {
        enumClass = constraintAnnotation.enumClass();
        allowedValues = constraintAnnotation.allowedValues();
        notAllowedValues = constraintAnnotation.notAllowedValues();
        keyColumn = constraintAnnotation.keyColumn();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if(value != null){
            // 校验enumClass中是否存在keyColumn字段
            if(!ReflectUtil.hasField(enumClass, keyColumn)){
                throw new RuntimeException(StrUtil.format("EnumValidator:<{}>中不存在填入的<{}>字段", enumClass.getSimpleName(), keyColumn));
            }
            // 获取所有该枚举的指定字段的值列表
            List codevalues = EnumUtil.getFieldValues(enumClass, keyColumn);

            String validatevalueMsg = ValidatorUtils.validatevalues(allowedValues, notAllowedValues, value, codevalues);
            if(StrUtil.isNotBlank(validatevalueMsg)){
                return ValidatorUtils.getResult(validatevalueMsg, context);
            }else{
                return true;
            }
        }
        return true;
    }
}
 
3. 使用示例 

枚举类

public enum SysDeptTypeEnum {
    SCHOOL(1, "学校"),
    INVALID(2, "单位/学院"),
    DEPARTMENT(3, "部门"),
    MAJOR(4, "专业"),

    ;
    private final int code;
    private final String name;

    SysDeptTypeEnum(int code, String info) {
        this.code = code;
        this.name = info;
    }

    public static String getValue(int code) {
        SysDeptTypeEnum[] enums = values();
        for (SysDeptTypeEnum item : enums) {
            if (item.code == code) {
                return item.getName();
            }
        }
        return null;
    }

    public int getCode() {
        return code;
    }

    public String getName() {
        return name;
    }
}

DTO请求参数实体类

@Data
public class SysDeptDto {


    
    @NotNull(message = "组织架构Id不能为空", groups = { EditGroup.class })
    private Long deptId;
    
    @NotNull(message = "组织架构类别不能为空", groups = { AddGroup.class, EditGroup.class })
    @VerifyEnum(enumClass = SysDeptTypeEnum.class, keyColumn = "code", groups = { AddGroup.class, EditGroup.class })
    private Integer type;
	
	// 其他属性省略...
}    

Controller接口

    @PostMapping()
    public ApiResult add(@Validated(AddGroup.class) @RequestBody SysDeptDto dto){
        return toApiRes(sysDeptService.insertByDto(dto));
    }

请求测试

稍微修改下注解代码

@VerifyEnum(enumClass = SysDeptTypeEnum.class, keyColumn = "code", notAllowedValues = {"5"}, groups = { AddGroup.class, EditGroup.class })

测试:

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5718089.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)