如果您的验证依赖于多个字段(例如 isPrimary 和 primaryDTO 或 secondaryDTO),那么唯一的解决方案是在类级别 (UserDTO) 上编写一个自定义验证器,它将实现条件验证自己。
例如,创建一个注解:
@Documented
@Retention(RUNTIME)
@Target({ANNOTATION_TYPE, TYPE})
@Constraint(validatedBy = SecondaryValidator.class)
public @interface ValidSecondary {
String message() default "Invalid secondary";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
并创建一个仅在isPrimary() 为false 时验证secondaryDTO 字段的验证器:
@Component
public class SecondaryValidator implements ConstraintValidator<ValidSecondary, UserDTO> {
private Validator validator;
public SecondaryValidator(Validator validator) {
this.validator = validator;
}
@Override
public boolean isValid(UserDTO userDTO, ConstraintValidatorContext constraintValidatorContext) {
if (userDTO.isPrimary()) {
return true;
} else {
return validator.validate(userDTO.getSecondaryDTO()).isEmpty();
}
}
}
之后,您可以从secondaryDTO 字段中删除@Valid 注释,并在UserDTO 顶部添加@ValidSecondary 注释:
@ValidSecondary // Add this
public class UserDTO {
@Valid
private PrimaryDTO primaryDTO;
private SecondaryDTO secondaryDTO; // No more @Valid
private boolean primary;
}
但是,在这种情况下,您将丢失来自 SecondaryDTO 内的任何约束违规消息,如果您想要某种传递机制,您可以将违规添加到 isValid() 内的 constraintValidatorContext方法,例如:
Set<ConstraintViolation<SecondaryDTO>> violations = validator.validate(userDTO.getSecondaryDTO());
violations.forEach(violation -> constraintValidatorContext
.buildConstraintViolationWithTemplate(violation.getMessage())
.addConstraintViolation());