Spring

첫 번째 유효성 검사기가 실패하면 두 번째 유효성 검사기 실행을 중지하는 방법은 무엇입니까?

기록만이살길 2021. 3. 8. 08:05
반응형

첫 번째 유효성 검사기가 실패하면 두 번째 유효성 검사기 실행을 중지하는 방법은 무엇입니까?

1. 질문(문제점):

Car 클래스가 있는데 두 개의 다른 사용자 지정 유효성 검사기에서 순서대로 유효성을 검사하고 싶습니다. 클래스 맨 위에 첫 번째 유효성 검사기를 설정하고 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>

첫 번째 유효성 검사기가 실패하면 두 번째 유효성 검사기를 실행하고 싶지 않습니다. 지금은 첫 번째가 실패하더라도 두 번째를 실행하고 두 유효성 검사기 모두에 대한 메시지를 반환합니다. 다음은 내 어노테이션 인터페이스와 컨트롤러 메서드입니다.

@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) {
  ...
}

2. 해결방안:

============================= 올바른 코드
내 코드는 dto "학생"을 사용하여 확인합니다.
학생 dto

@Getter
@Setter
public class Student implements Serializable{

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

}

StudentValidator

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");
        }
    }
}

StudentController

@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"에 게시하고 매개 변수가 없습니다. 다음과 같이 응답

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

===================================== 아래는 옳지 않습니다
WebDataBinder를 사용해야한다고 생각합니다 이를 구현하려면 validate () 메서드를 재정의합니다. 여기에서 참조 하십시오 . 아래는 의사 코드를 제공합니다.

  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
    }

65680922
반응형