如果第一个验证器失败,如何停止执行第二个验证器?

How to stop execution of the second validator if first one fails?

我有一辆汽车 class,我希望它由两个不同的自定义验证程序按顺序进行验证。我将第一个验证器设置在 class 之上,另一个来自 validation-constraints-car.xml 文件。

@Validator1
public class Car {
    private static final long serialVersionUID = 5535968331666441498L;
    
    ...
}
<constraint-mappings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd"
                     xmlns="http://jboss.org/xml/ns/javax/validation/mapping">
    <bean class="com.galery.Car" ignore-annotations="false">
        <class ignore-annotations="false">
            <constraint annotation="com.galery.validation.specific.Validator2"></constraint>
        </class>
    </bean>

</constraint-mappings>

当第一个验证器失败时,我不想执行第二个验证器。现在,即使第一个失败,它也会执行第二个和 returns 两个验证器的消息。这是我的注释接口和控制器方法。

@RequestMapping(value = "....", method = RequestMethod.POST)
public void validateCar(@Valid @RequestBody Car car) {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(
    validatedBy = {Validator1Impl.class}
)
public @interface Validator1{
    String message() default "{validator1.message}";

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

    Class<? extends Payload>[] payload() default {};
}
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(
        validatedBy = {Validator2Impl.class}
)
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public @interface Validator2{
    String message() default "{validator1.message}";

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

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

我怎样才能达到我想要的?有什么方法可以在 ConstraintValidatorContext 中查找先前验证器的消息吗?

 @Override
 public boolean isValid(Car value, ConstraintValidatorContext context) {
  ...
}

==============================正确代码
我的代码使用 dto "student" 来验证。
学生 dto

@Getter
@Setter
public class Student implements Serializable{

    private static final long serialVersionUID = 1L;
    
    private String name;
    private Integer grade;

}

学生验证器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;


@Component
public class StudentValidator implements Validator {

    
    @Override
    public boolean supports(Class<?> clazz) {
        boolean result =  Student.class.equals(clazz);
        return result;
    }

    @Override
    public void validate(Object target, Errors errors) {
        Student student = (Student) target;
        ValidationUtils.rejectIfEmpty(errors, "name","name must not be empty");
    }
}

StudentValidator2。如果有错误,不验证。

@Component
public class StudentValidator2 implements Validator {

    @Autowired
    private MessageSource messageSource;
    
    @Override
    public boolean supports(Class<?> clazz) {
        boolean result =  Student.class.equals(clazz);
        return result;
    }

    @Override
    public void validate(Object target, Errors errors) {
        if(errors.hasErrors()){ // if having error, not go ahead
            return ;
        }
        
        Student student = (Student) target;
        if (student.getGrade() <= 0) {
            errors.rejectValue("grade", "grade must be more than 0");
        }
    }
}

学生控制器

@RestController("/")
public class StudentController {

    @Autowired
    private StudentValidator studentValidator;

    @Autowired
    private StudentValidator2 studentValidator2;

    @InitBinder(value = "student")
    void initStudentValidator(WebDataBinder binder) {
        binder.addValidators(studentValidator, studentValidator2);

    }

    @PostMapping("/student")
    public ResponseEntity<Student> saveStudent(@RequestBody @Valid Student student) {
        // Other logic here(Calling the service layer,etc.)
        return new ResponseEntity<>(student, HttpStatus.CREATED);
    }
}

使用 postMan 对“/student”执行 post 并且没有参数。其响应如下

{
    "status": "BAD_REQUEST",
    "error": "Validation failed",
    "count": 1,
    "errors": [
        "name must not be empty"
    ]
}

=======================================下面不对
我认为您应该使用 WebDataBinder 和 Override validate() 方法来实现它。请参考 here。下面我给出一些伪代码

  1. 创建一个名为 MyWebDataBinder 的自定义 WebDataBinder。在那里,重写 validate() 方法。
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.WebDataBinder;

@Component
public class MyWebDataBinder extends WebDataBinder {

    public MyWebDataBinder(Object target) {
        super(target);
    }

    public MyWebDataBinder(Object target, String objectName) {
        super(target, objectName);
    }

    @Override
    public void validate() {
        Object target = getTarget();
        Assert.state(target != null, "No target to validate");
        BindingResult bindingResult = getBindingResult();
        // Call each validator with the same binding result
        for (Validator validator : getValidators()) {
            validator.validate(target, bindingResult);
            if(bindingResult.hasErrors()) // jump out when if having error 
                break;
        }
    }
}
  1. 在你的控制器中,注入 MyWebDataBinder 和 Validator1 & Validator2

@Controller
public class CustomerController {

   @Autowired
   Validator1 validator1

   @Autowired
   Validator2 validator2

    @InitBinder(value = "car")
    void initCarValidator(@Qualifier("myWebDataBinder")WebDataBinder binder) {
        binder.addValidators(validator1);
        binder.addValidators(validator2);
    }

    @RequestMapping(value = "....", method = RequestMethod.POST)
     public void validateCar(@Valid @RequestBody Car car) {
       // .... your code
    }