我在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;
}