在类和字段上使用GroupSequence和相同的自定义批注JRS-303的顺序

时间:2018-11-16 18:59:15

标签: java validation jsr380

我在Spring Boot项目中使用JSR-380规范来验证rest api请求。

我在类和字段上都使用了自定义注释。由于在类上可以有相同的验证器,而对于具有不同消息的同一字段可以有相同的验证器,所以我想为自定义验证器定义groupSequence。我只想在类验证成功的情况下验证字段验证,反之亦然,如果序列颠倒,则相反。您可以看到我尝试使用groupSequence的方式,假设所有Default和others接口都可用。

我在以下不同情况下遇到以下错误:

1。错误:

"message": "class javax.validation.GroupDefinitionException
    HV000053: 'Default.class' cannot appear in default group sequence
    list."

我在使用时

@GroupSequence({Default.class, ClassFirst.class, FieldSecond.class})

2。错误:

"message": "class javax.validation.GroupDefinitionException
    HV000054: package.Request must be part of the
    redefined default group sequence."

当我使用

@GroupSequence({ClassFirst.class, FieldSecond.class})

我的代码如下:

注释:

@Documented
@Constraint(validatedBy = { SpecificValueValidator.class })
@Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@ReportAsSingleViolation
public @interface SpecificValue {
    String message() default "{xyz.util.SpecificValue}";

    BusinessErrors enumMessage();

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

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

    String fieldName() default StringUtils.EMPTY;

    String value() default StringUtils.EMPTY;

    String pattern() default StringUtils.EMPTY;

    boolean isNullable() default false;

    @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        SpecificValue[] value();
    }
}

验证者:

public class SpecificValueValidator implements ConstraintValidator<SpecificValue, Object> {
    private String fieldName;
    private String value;
    private String pattern;
    private boolean isNullable;

    @Override
    public void initialize(SpecificValue annotation) {
        this.fieldName = annotation.fieldName();
        this.value = annotation.value();
        this.pattern = annotation.pattern();
        this.isNullable = annotation.isNullable();
    }

    @Override
    public boolean isValid(Object object, ConstraintValidatorContext ctx) {
        boolean isValid = true;
        Object fieldValue;
        try {
            if (StringUtils.isNotBlank(fieldName)) {
                fieldValue = BeanUtils.getProperty(object, fieldName);
            } else {
                fieldValue = object;
            }
        } catch(NestedNullException | NullPointerException ex) {
            fieldValue = null;
        }catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }

        if (fieldValue == null && BooleanUtils.isTrue(isNullable)) {
            isValid = true;
        } else if (fieldValue instanceof String && StringUtils.isBlank((String) fieldValue) && BooleanUtils.isTrue(isNullable)) {
            isValid = true;
        } else {
            //fieldValue should not be null or blank else return isValid
            isValid = fieldValue instanceof String ? StringUtils.isNotBlank((String) fieldValue) : fieldValue != null;
            //fieldValue should match with specific value else return isValid
            isValid = fieldValue instanceof String && StringUtils.isNotBlank(value) ? StringUtils.equalsIgnoreCase(value, (String) fieldValue): isValid;
            //feildValue should match with specific pattern else return isValid
            isValid = fieldValue instanceof String && StringUtils.isNotBlank(pattern) ? ((String) fieldValue).matches(pattern) : isValid;
            //if specific value contains multiple value separated by comma
            if(StringUtils.isNotBlank(value)) {
                List<String> values = Arrays.asList(value.toLowerCase().split("\\s*,\\s*"));
                isValid = fieldValue instanceof String ? values.contains(((String) fieldValue).toLowerCase()) : isValid;
            }
        }
        //if isValid is false mark corresponding field as dirty
        if (BooleanUtils.isFalse(isValid) && StringUtils.isNotBlank(fieldName)) {
            ctx.disableDefaultConstraintViolation();
            ctx.buildConstraintViolationWithTemplate(ctx.getDefaultConstraintMessageTemplate())
                    .addPropertyNode(fieldName)
                    .addBeanNode()
                    .addConstraintViolation();
        }
        return isValid;
    }
}

请求:

@GroupSequence({Default.class, ClassFirst.class, FieldSecond.class})
@SpecificValue.List({
        @SpecificValue(enumMessage = BusinessErrors.XYZ1_NAME_TYPE_SHOULD_BE_I, fieldName = "xyz1.personName.nameTypeInd", value = "I")
    })

public class Request implements Serializable {
    @Valid
    @NotNullable(enumMessage = RequiredFieldErrors.XYZ1_INFO)
    private PersonType xyz1;
    @Valid
    @NotNullable(enumMessage = RequiredFieldErrors.XYZ2_INFO)
    private PersonType xyz2;

}
public class PersonType implements Serializable {
    @Valid
    @NotNullable(enumMessage = RequiredFieldErrors.PERSON_NAME_R)
    private PersonName personName;
    @Valid
    @NotNullable(enumMessage = RequiredFieldErrors.PERSON_NAME_R)
    private PersonAddress personAddress;
}

public class PersonName implements Serializable {
    @SpecificValue(enumMessage = BusinessErrors.NAME_TYPE_INIDICATOR, value = ConstantValue.NAME_TYPE_VALUES, groups=FieldSecond.class) // Here nameType can be B, I for xyz2 and others, however, only I for xyz1
    private String nameTypeInd;
}

0 个答案:

没有答案