自定义参数校验注解-条件校验

validation 为我们提供了这么多特性,几乎可以满足日常开发中大多数参数校验场景了,如@NotNull,@NotBlank等。 但是在实际开发过程中如字段A满足条件,才对字段B进行校验这种场景也应用的比较多。然validation 库并没有提供这样的校验注解,一般需要在业务逻辑里面做判断。validation 也提供了扩展,允许用户自定义校验。接下来实现参数自定义条件校验注解。

1 场景

场景: 请求参数对象中,当属性A= x时, 才校验属性B
示例: 当type= 1 时, username必填, 否则username可不填
当type = 2 时, status必填且允许填值1或者2,不能填值3和4

@ApiModelProperty("类型")
@NotNull(message = "类型不能为空")
private Integer type;

@ApiModelProperty("用户名")
@ConditionBy(column = "type",columnValue = "1",message = "用户名不能为空")
private String username;


/**
*  allowedValues --条件触发时,只允许填的值(被注解属性不为对象)  (选填)
*  unAllowedValues  --条件触发时,不允许填的值(被注解属性不为对象)  (选填)
*/
@ApiModelProperty("状态")
@ConditionBy(column = "type",columnValue = "2", allowedValues = {"1","2"},
             unAllowedValues = {"3","4"},message = "状态不能为空")
private String status;

2 自定义注解

/**
 *  该注解用于参数的关联校验
 *   型如: 属性A=1时,对属性B进行校验
 * @Author: zhangxilin
 * @Date: 2023/6/27
 */
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ConditionBy.List.class)
@Constraint(validatedBy = {ConditionByValidator.class})
public @interface ConditionBy {

    /**
     * 决定该字段需要关联的字段名
     * @return
     */
    String column();

    /**
     * column的会触发该校验校的条件值类型
     * @return
     */
    ConditionType columnValueType() default ConditionType.FIXED;
    /**
     *  column的会触发该校验的条件值(ConditionType.FIXED是必填)
     * @return
     */
    String[] columnValue() default {};

    /**
     * 条件触发时,只允许填的值(被注解属性不为对象)
     */
    String[] allowedValues() default { };

    /**
     * 条件触发时,不允许填的值(被注解属性不为对象)
     */
    String[] unAllowedValues() default { };

    /**
     * 自定义错误信息
     */
    String message() default "不合法的参数";

    /**
     * 忽略错误消息详情,为false时,返回错误消息详情
     * @return
     */
    boolean ignoreMsgDetail() default true;
    /**
     *   是否校验属性,开启则对其校验,不开启只判空。(一般需要校验对象或是对象列表时使用,默认关闭)
     * @return
     */
    boolean isCheck() default false;

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

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

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface List {
        ConditionBy[] value();
    }

    enum ConditionType{
    NULL, //column对应字段值为空时触发校验
    UNNULL, //column对应字段值不为空时触发校验
    FIXED //column对应字段值为某个固定值(columnValue的值)时触发校验
}

3 校验逻辑

@Slf4j
public class ConditionByValidator implements ConstraintValidator<ConditionBy,Object> {
    private String column;
    private String[] columnValue;
    private ConditionBy.ConditionType columnValueType ;
    private String[] allowedValues;
    private String[] unAllowedValues;
    private boolean isCheck;
    private String message;
    private boolean ignoreMsgDetail;
    private static Validator validator;
    private static final String NULL_STR = "null";



    @Override
    public void initialize(ConditionBy constraintAnnotation) {
        log.info("IsAllowByValidator>>>>>>>>>>>>>>>initialize");
        column = constraintAnnotation.column();
        columnValueType = constraintAnnotation.columnValueType();
        columnValue = constraintAnnotation.columnValue();
        allowedValues = constraintAnnotation.allowedValues();
        unAllowedValues = constraintAnnotation.unAllowedValues();
        isCheck = constraintAnnotation.isCheck();
        message = constraintAnnotation.message();
        ignoreMsgDetail = constraintAnnotation.ignoreMsgDetail();
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
        factory.close();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        // 获取column的请求值,为空的话校验失败
        String columnRequestValue = getColumnRequestValue();

        if(columnValueType == ConditionBy.ConditionType.NULL){
            if(StringUtils.isNotBlank(columnRequestValue)){
                //column字段值为空时校验
                return true;
            }
        }else if(columnValueType == ConditionBy.ConditionType.UNNULL){
            if(StringUtils.isBlank(columnRequestValue)){
                //column字段值不为空时校验
                return true;
            }
        }else {
            if(StringUtils.isBlank(columnRequestValue)){
                return ValidatorUtils.getResult(StrUtil.format("请求值{}不能为空",column),context);
            }
            if(columnValue ==null || columnValue.length ==0){
                return ValidatorUtils.getResult("请填写条件校验触发值columnValue",context);
            }
            if(!Arrays.asList(columnValue).contains(columnRequestValue)){
                // 如果column的值不存在于columnValue中 不校验
                return true;
            }
        }

        if(ObjectUtil.isEmpty(value)){
            // 被注解字段的值为空直接校验不通过
            return false;
        }
        // notAllowedValues、allowedValues存在情况
        boolean b = (unAllowedValues != null && unAllowedValues.length > 0) || (allowedValues != null && allowedValues.length > 0);
        if(b){
            String validResult = ValidatorUtils.validateValues(allowedValues,unAllowedValues,value);
            if(StringUtils.isNotBlank(validResult)){
                String msg = ignoreMsgDetail?message:message+ "且"+validResult;
                return ValidatorUtils.getResult(msg, context);
            }
        }
        // 如果开启校验
        if(isCheck){
            return validObject(value,context);
        }
            return true;
        }

            private String getColumnRequestValue(){
            String paramValue = null;
            try {
            paramValue = getParameter(column);
            // 如果从param获取不到,再找body
            if(StringUtils.isBlank(paramValue)){
            String body = new RequestWrapper(getRequest()).getBody();
            if(StringUtils.isNotBlank(body)){
            JSONObject jsonObject = JSONUtil.parseObj(body);
            Object paramValueObject= jsonObject.get(column);
            if(ObjectUtil.isEmpty(paramValueObject)){
            return null;
        }
            if(NULL_STR.equalsIgnoreCase(paramValueObject+"")){
            return null;
        }
            paramValue = String.valueOf(paramValueObject);
        }
        }
        } catch (Exception e) {
            e.printStackTrace();
        }
            return paramValue;
        }



            /**
            * 校验传入对象
            *
            * @param o  当前字段的值
            * @param context  ConstraintValidatorContext校验上下文
            * @return boolean
            * @date 2023/9/20
            **/
            private boolean validObject(Object o, ConstraintValidatorContext context){
            // 定义错误信息
            StringBuffer errorMsg = new StringBuffer();
            boolean result = true;
            // 当被注解属性为列表时
            if(o instanceof ArrayList<?>){
            List<Set<ConstraintViolation<Object>>> collect = new ArrayList<>();
            for (Object oItem : (List<?>) o){
            Set<ConstraintViolation<Object>> constraintViolations = validator.validate(oItem);
            collect.add(constraintViolations);
        }
            result = ValidatorUtils.getListResult(collect);
            ValidatorUtils.getListInfo(errorMsg, collect);
        }else {
            // 当被注解属性为单个对象时
            Set<ConstraintViolation<Object>> constraintViolations = validator.validate(o);
            result = constraintViolations.isEmpty();
            ValidatorUtils.getOneInfo(errorMsg, constraintViolations);
        }

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


            /**
            * 获取request
            */
            public static HttpServletRequest getRequest() {
            return getRequestAttributes().getRequest();
        }
            public static ServletRequestAttributes getRequestAttributes() {
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            return (ServletRequestAttributes) attributes;
        }
            public static String getParameter(String name) {
            return getRequest().getParameter(name);
        }

        }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值