如果第一个验证器失败,如何停止执行第二个验证器?
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。下面我给出一些伪代码
- 创建一个名为 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;
}
}
}
- 在你的控制器中,注入 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
}
我有一辆汽车 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。下面我给出一些伪代码
- 创建一个名为 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;
}
}
}
- 在你的控制器中,注入 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
}