Spring 依赖于其他字段的自定义验证器

Spring custom validator with dependencies on other fields

我们正在为控制器端点中使用的请求对象使用 spring 自定义验证器。我们以与下面 link 中相同的方式实现它:

https://www.baeldung.com/spring-mvc-custom-validator

我们面临的问题是,如果特定字段也依赖于其他输入字段,它就无法工作。例如,我们将以下代码作为控制器端点的请求对象:

public class FundTransferRequest {

     private String accountTo;
     private String accountFrom;
     private String amount;
     
     @CustomValidator
     private String reason;

     private Metadata metadata;

}

public class Metadata {
   private String channel; //e.g. mobile, web, etc.
}

基本上@CustomValidator 是我们的自定义验证器class,我们想要的逻辑是,如果元数据提供的渠道是“WEB”。请求的“原因”字段将不是必需的。否则,它将是必需的。

有办法吗?我已经进行了额外的研究,但看不到任何可以处理此类情况的方法。

显然,如果您需要访问自定义验证器中的多个字段,则必须使用 class-level 注释。

您提到的同一篇文章有​​一个例子:https://www.baeldung.com/spring-mvc-custom-validator#custom-class-level-validation

在您的情况下,它可能看起来像这样:

@Constraint(validatedBy = CustomValidator.class)
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomValidation {

    String message() default "Reason required";
    String checkedField() default "metadata.channel";
    String checkedValue() default "WEB";
    String requiredField() default "reason";

    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
package com.example.demo;

import org.springframework.beans.BeanWrapperImpl;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/*
If the supplied channel from Metadata is "WEB". The field "reason" of the request won't be required.
Else, it will be required.
 */
@Component
public class CustomValidator implements ConstraintValidator<CustomValidation, Object> {
    private String checkedField;
    private String checkedValue;
    private String requiredField;

    @Override
    public void initialize(CustomValidation constraintAnnotation) {
        this.checkedField = constraintAnnotation.checkedField();
        this.checkedValue = constraintAnnotation.checkedValue();
        this.requiredField = constraintAnnotation.requiredField();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        Object checkedFieldValue = new BeanWrapperImpl(value)
                .getPropertyValue(checkedField);
        Object requiredFieldValue = new BeanWrapperImpl(value)
                .getPropertyValue(requiredField);

        return checkedFieldValue != null && checkedFieldValue.equals(checkedValue) || requiredFieldValue != null;
    }
}

用法为:

@CustomValidation
public class FundTransferRequest {
...

或指定参数:

@CustomValidation(checkedField = "metadata.channel", 
        checkedValue = "WEB", 
        requiredField = "reason", 
        message = "Reason required")
public class FundTransferRequest {
...