Springboot实现 其他属性条件 校验注解

Springboot实现 其他属性条件 校验注解,第1张

Springboot实现 其他属性条件 校验注解

文章目录

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

2.1 实现思路2.2 代码实现

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

1. 使用场景

例如:当属性condition=1时,某某参数为必填;某某参数为必填且只能为某些值或不能为某些值。


支持扩展场景:

    只允许填入某些值不允许填入某些值如果使用注解的属性是对象,可以控制是否对对象中的属性进行再校验
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.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(OtherAffect.List.class)
@Constraint(validatedBy = {OtherAffectValidator.class})
public @interface OtherAffect {

    
    String column();

    
    String[] columnValue();

    
    String[] allowedValues() default { };

    
    String[] notAllowedValues() default { };

    
    String message() default "";

    
    boolean isCheck() default false;

    Class[] groups() default { };

    Class[] payload() default { };

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface List {
        OtherAffect[] 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 OtherAffectValidator implements ConstraintValidator {


    private String[] columnValue;

    private String column;

    private boolean isCheck;

    private String[]  allowedValues;

    private String[]  notAllowedValues;

    private static Validator validator;

    
    @Override
    public void initialize(OtherAffect constraintAnnotation) {
        columnValue = constraintAnnotation.columnValue();
        column = constraintAnnotation.column();
        isCheck = constraintAnnotation.isCheck();
        allowedValues = constraintAnnotation.allowedValues();
        notAllowedValues = constraintAnnotation.notAllowedValues();
        // 获取校验Validator
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
        factory.close();
    }

    
    @Override
    public boolean isValid(Object o, ConstraintValidatorContext context) {
        // 获取column的请求值,为空的话校验失败
        String columnRequestValue = getColumnRequestValue();
        if (StrUtil.isBlank(columnRequestValue)) {
            return ValidatorUtils.getResult(StrUtil.format("请求值{}不能为空", column), context);
        }

        // 如果column的值存在于columnValue中
        if (Arrays.asList(columnValue).contains(columnRequestValue)) {
            // 被注解字段的值为空直接校验不通过
            if (ObjectUtil.isEmpty(o)) {
                return false;
            }
            // notAllowedValues、allowedValues存在情况
            boolean b = (notAllowedValues != null && notAllowedValues.length > 0) || (allowedValues != null && allowedValues.length > 0);
            if (b) {
                String validResult = ValidatorUtils.validatevalues(allowedValues, notAllowedValues, o);
                return ValidatorUtils.getResult(validResult, context);
            }

            // 如果开启校验
            if (isCheck) {
                return validObject(o, context);
            }
        }
        return true;
    }

    
    private String getColumnRequestValue(){
        String paramValue = ServletUtils.getParameter(column);
        // 如果从param获取不到,再找body
        if(StringUtils.isBlank(paramValue)){
            HttpServletRequest request = ServletUtils.getRequest();
            // 获取column请求参数值
            String body = ServletUtil.getBody(request);
            if(StrUtil.isNotBlank(body)){
                JSONObject jsonObject = JSONUtil.parseObj(body);
                paramValue = String.valueOf(jsonObject.get(column));
            }
        }
        return paramValue;
    }

    
    private boolean validObject(Object o, ConstraintValidatorContext context){
        // 定义错误信息
        StringBuffer errorMsg = new StringBuffer();
        boolean result = true;
        // 当被注解属性为列表时
        if(o instanceof ArrayList){
            List>> collect = new ArrayList<>();
            for (Object oItem : (List) o){
                Set> constraintViolations = validator.validate(oItem);
                collect.add(constraintViolations);
            }
            result = getListResult(collect);
            ValidatorUtils.getListInfo(errorMsg, collect);
        }else {
            // 当被注解属性为单个对象时
            Set> constraintViolations = validator.validate(o);
            result = constraintViolations.isEmpty();
            ValidatorUtils.getOneInfo(errorMsg, constraintViolations);
        }

        // 把自定义返回错误信息写入上下文
        if(!result){
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(errorMsg.toString())
                    .addConstraintViolation();
        }
        return result;
    }


    
    private boolean getListResult(List>> collect){
        boolean result = true;
        for (Set> constraintViolations : collect) {
            result = constraintViolations.isEmpty();
        }
        return result;
    }

3. 使用示例
@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;
    
    @OtherAffect(column = "type", columnValue = {"2", "3", "4"}, message = "父id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long parentId;
}

解释:
当传入的type值为2、3、4时,这个父id传入值不能为空

其他扩展用法,有兴趣可以复制代码尝试!

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

原文地址: https://outofmemory.cn/zaji/5718098.html

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

发表评论

登录后才能评论

评论列表(0条)