복붙노트

[SPRING] 여러 필드가있는 사용자 정의 주석 유효성 검사

SPRING

여러 필드가있는 사용자 정의 주석 유효성 검사

조금 욕심 많은 질문이 있습니다.이 주석이 주석 유효성 검사에 대해 더 알고 싶은 사람들에게도 도움이되기를 바랍니다.

저는 현재 Spring을 연구 중입니다. 현재는 주석이 달린 검증을 맞춤화해볼 계획입니다.

필자는 많은 검색을 해왔고 이제는 주로 두 종류의 유효성 검사가 있다는 것을 알고 있습니다. 하나는 컨트롤러에 사용되고 다른 하나는 @Valid를 사용하는 주석 메서드입니다.

여기 내 시나리오가 있습니다. 두 개 이상의 필드가 있고 모두 NULL 일 때 null이 될 수 있다고 가정합니다. 그러나 빈 문자열을 제외한 값이 필드 중 하나에 포함되어있는 경우에만 해당 필드에 입력해야합니다. 그리고 두 가지 아이디어가 있지만 올바르게 구현하는 방법을 알지 못했습니다.

다음은 클래스 예제입니다.

public class Subscriber {
    private String name;
    private String email;
    private Integer age;
    private String phone;
    private Gender gender;
    private Date birthday;
    private Date confirmBirthday;
    private String birthdayMessage;
    private Boolean receiveNewsletter;

    //Getter and Setter
}

birthday 필드와 confirmBirthday 필드가 모두 null이거나 반대가 될 필요가 있다고 가정하면, 각각에 대해 하나의 주석을 사용하여 주석을 달고 다음과 같이 보일 수 있습니다.

public class Subscriber {
    private String name;
    private String email;
    private Integer age;
    private String phone;
    private Gender gender;

    @NotNullIf(fieldName="confirmBirthday")
    private Date birthday;

    @NotNullIf(fieldName="birthday")
    private Date confirmBirthday;

    private String birthdayMessage;
    private Boolean receiveNewsletter;

    //Getter and Setter
}

그래서 나는 이렇게 유효성 검사 Annotation을 만들어야한다 :

@Documented
@Constraint(validatedBy = NotNullIfConstraintValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD })
public @interface NotNullIf {

    String fieldName();

    String message() default "{NotNullIf.message}";
    Class<?>[] group() default {};
    Class<? extends Payload>[] payload() default {};
}

그리고 나서 Validator 자체를 만들어야합니다.

public class NotNullIfConstraintValidator implements ConstraintValidator<NotNullIf, String>{

    private String fieldName;

    public void initialize(NotNullIf constraintAnnotation) {
        fieldName = constraintAnnotation.fieldName();
    }

    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(value == null) {
            return true;
        };
        //TODO Validation
        return false;
    }

}

동일한 클래스를 사용하여 생일을 원한다고 말한 또 다른 아이디어의 경우 confirmBirthday와 birthdayMessdage는 null이거나 동시에 반대 일 수 있습니다. 이번에는 교차 필드 유효성 검사를 위해 클래스 주석이 적용된 유효성 검사를 사용해야 할 수도 있습니다.

다음은 클래스에 주석을 추가하는 방법입니다.

@NotNullIf(fieldName={"birthday", "confirmBirthday", "birthdayMessage"})
public class Subscriber {
    //Those field same as the above one
}

따라서 해당 필드 중 하나가 null이 아닌 경우 나머지는 또한 클라이언트 크기로 입력해야합니다. 가능한가?

이 기사를 읽었습니다 : 주석 속성에 설명 된 필드에 액세스하는 방법

그러나 위에 나열된 요소에서 주석 유효성 검사가 어떻게 작동하는지 아직도 혼란 스럽습니다. 어쩌면 그 코드에 대한 자세한 설명이 필요하거나 더 나쁘게 기본 개념 검사가 필요할 수도 있습니다.

도와주세요!

해결법

  1. ==============================

    1.이를 위해 필드 레벨 주석에 다른 필드에 대한 액세스 권한이 없기 때문에 유형 레벨 주석 만 사용할 수 있습니다!

    이를 위해 필드 레벨 주석에 다른 필드에 대한 액세스 권한이 없기 때문에 유형 레벨 주석 만 사용할 수 있습니다!

    선택 유효성 검사를 허용하는 것과 비슷한 작업을 수행했습니다 (정확히 여러 속성 중 하나가 null이 아니어야 함). 귀하의 경우 @AllOrNone 주석 (또는 선호하는 이름)은 필드 이름의 배열을 필요로하고 주석 처리 된 유형의 전체 객체를 유효성 검사기로 가져옵니다.

    @Target(ElementType.TYPE)
    @Retention(RUNTIME)
    @Documented
    @Constraint(validatedBy = AllOrNoneValidator.class)
    public @interface AllOrNone {
        String[] value();
    
        String message() default "{AllOrNone.message}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    
    public class AllOrNoneValidator implements ConstraintValidator<AllOrNone, Object> {
        private static final SpelExpressionParser PARSER = new SpelExpressionParser();
        private String[] fields;
    
        @Override
        public void initialize(AllOrNone constraintAnnotation) {
            fields = constraintAnnotation.value();
        }
    
        @Override
        public boolean isValid(Object value, ConstraintValidatorContext context) {
            long notNull = Stream.of(fields)
                    .map(field -> PARSER.parseExpression(field).getValue(value))
                    .filter(Objects::nonNull)
                    .count();
            return notNull == 0 || notNull == fields.length;
        }
    }
    

    (스프링을 사용한다고 말했듯이 SpEL을 사용하여 중첩 된 필드에 액세스 할 수도있었습니다)

    이제 구독자 유형에 주석을 달 수 있습니다.

    @AllOrNone({"birthday", "confirmBirthday"})
    public class Subscriber {
        private String name;
        private String email;
        private Integer age;
        private String phone;
        private Gender gender;
        private Date birthday;
        private Date confirmBirthday;
        private String birthdayMessage;
        private Boolean receiveNewsletter;
    }
    
  2. from https://stackoverflow.com/questions/40353638/spring-custom-annotation-validation-with-multiple-field by cc-by-sa and MIT license