首页 > 解决方案 > 基于 Spring 自定义条件的验证

问题描述

我想在 spring 验证器中创建基于条件的验证。我有一个UserDTO类,其中有两个带@Valid注释的 DTO 类。

如果我通过isPrimary true那么它应该只验证primaryDTO bean 并忽略secendoryDTO验证。

public class UserDTO {
    @Valid
    private PrimaryDTO primaryDTO;
    @Valid
    private SecendoryDTO secendoryDTO;
    private boolean isPrimary;
}

public class PrimaryDTO {
    @NotEmpty(message = "Please enter email.")
    @Email(message = "Please enter a valid email.")
    private String email;
}

public class SecendoryDTO {
    @NotEmpty(message = "Please enter phone.")
    private String phone;
}

请指导。

谢谢

标签: javaspringspring-mvcspring-bootspring-validator

解决方案


如果您的验证依赖于多个字段(例如isPrimaryprimaryDTO或者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 {};
}

并创建一个验证器,仅在issecondaryDTO时验证该字段:isPrimary()false

@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();
        }
    }
}

之后,您可以@ValidsecondaryDTO字段中删除注释并将@ValidSecondary注释添加到您的顶部UserDTO

@ValidSecondary // Add this
public class UserDTO {
    @Valid
    private PrimaryDTO primaryDTO;
    private SecondaryDTO secondaryDTO; // No more @Valid
    private boolean primary;
}

但是,在这种情况下,您将丢失 中的任何约束违规消息SecondaryDTO,如果您想要某种传递机制,您可以将违规添加到constraintValidatorContext方法isValid()中,例如:

Set<ConstraintViolation<SecondaryDTO>> violations = validator.validate(userDTO.getSecondaryDTO());
violations.forEach(violation -> constraintValidatorContext
    .buildConstraintViolationWithTemplate(violation.getMessage())
    .addConstraintViolation());

推荐阅读