Home Custom Email Validator
Post
Cancel

Custom Email Validator

@Email

  • 기존 @Email 의 문제
    • 해당 검증기를 구현하고있는 EmailValidator.class안에 isValid 부분에서 if(value == null) return true;
      부분이 email이 등록 안되어 있을때에 검증기가 통과됨. 개발중인 서비스에서 사용하려는 @Email은 PK로 작용해야 하기 때문에 커스텀 필요
  • 문제가된 EmailValidator.class 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class EmailValidator extends AbstractEmailValidator<Email> {

  private static final Log LOG = LoggerFactory.make(MethodHandles.lookup());

  private java.util.regex.Pattern pattern;

  @Override
  public void initialize(Email emailAnnotation) {
    super.initialize(emailAnnotation);

    Pattern.Flag[] flags = emailAnnotation.flags();
    int intFlag = 0;
    for (Pattern.Flag flag : flags) {
      intFlag = intFlag | flag.getValue();
    }

    // we only apply the regexp if there is one to apply
    if (!".*".equals(emailAnnotation.regexp()) || emailAnnotation.flags().length > 0) {
      try {
        pattern = java.util.regex.Pattern.compile(emailAnnotation.regexp(), intFlag);
      } catch (PatternSyntaxException e) {
        throw LOG.getInvalidRegularExpressionException(e);
      }
    }
  }

  @Override
  public boolean isValid(CharSequence value, ConstraintValidatorContext context) {

    if (value == null) {  //  해당 부분
      return true;    //  해당 부분
    }  //  해당 부분

    boolean isValid = super.isValid(value, context);
    if (pattern == null || !isValid) {
      return isValid;
    }

    Matcher m = pattern.matcher(value);
    return m.matches();
  }
}


Custom @Email

  • @NotNullEmail 검증기를 생성
    • 1 ] @Email : 코드를 그대로 긁어서 해당 검증 부분만 변경
    • 2 ] 검증기 등록
    • 3 ] 테스트용 ExceptionHandler 생성, 및 테스트 코드 작성.


Custom @NotNullEmail : @interface 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Documented
@Constraint(validatedBy = {NotNullEmailValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Repeatable(List.class)
public @interface NotNullEmail {
  String message() default "{jakarta.validation.constraints.Email.message}";

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

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

  String regexp() default ".*";

  Pattern.Flag[] flags() default {};

  @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  @Retention(RUNTIME)
  @Documented
  public @interface List {
    NotNullEmail[] value();
  }

}


NotNullEmailValidator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Slf4j
public class NotNullEmailValidator extends AbstractEmailValidator<NotNullEmail> {

  private java.util.regex.Pattern pattern;

  @Override
  public void initialize(NotNullEmail emailAnnotation) {
    super.initialize(emailAnnotation);

    Pattern.Flag[] flags = emailAnnotation.flags();
    int intFlag = 0;
    for (Pattern.Flag flag : flags) {
      intFlag = intFlag | flag.getValue();
    }

    if (!".*".equals(emailAnnotation.regexp()) || emailAnnotation.flags().length > 0) {
      try {
        pattern = java.util.regex.Pattern.compile(emailAnnotation.regexp(), intFlag);
      } catch (PatternSyntaxException e) {
        log.error("patternSyntaxException = ", e);
      }
    }
  }

  @Override
  public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
    if (value == null || value == "") {
      return false;
    }

    boolean isValid = super.isValid(value, context);
    if (pattern == null || !isValid) {
      return isValid;
    }

    Matcher m = pattern.matcher(value);
    return m.matches();
  }

}


테스트 컨트롤러

  • @PostMapping("/validator") : 테스트용 handler
  • TestReq.class : 검증기 테스트용 req 생성
  • handleValidationExceptions() : 메세지 검증용 핸들러
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@RestController
public class ValidatorTestController {

  @PostMapping("/validator")
  public ResponseEntity<String> addUser(@Valid @RequestBody TestReq testReq) {
    return ResponseEntity.ok("valid ok");
  }

  @Getter
  @Setter
  @ToString
  @NoArgsConstructor
  @AllArgsConstructor
  public static class TestReq {
    @NotNullEmail(message = "is not null!!")
    private String email;
  }

  @ResponseStatus(HttpStatus.BAD_REQUEST)
  @ExceptionHandler(MethodArgumentNotValidException.class)
  public Map<String, String> handleValidationExceptions(
    MethodArgumentNotValidException ex) {
    Map<String, String> errors = new HashMap<>();
    ex.getBindingResult().getAllErrors().forEach((error) -> {
      String fieldName = ((FieldError) error).getField();
      String errorMessage = error.getDefaultMessage();
      errors.put(fieldName, errorMessage);
    });
    return errors;
  }

}


테스트 코드

  • isBadRequest() : HttpStatus 검증
  • is("is not null!!") : 검증 메세지 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@SpringBootTest
@AutoConfigureMockMvc
public class ValidatorTest {

  @Autowired
  ValidatorTestController validatorTestController;

  @Autowired
  private MockMvc mockMvc;

  @Autowired
  private ObjectMapper objectMapper;

  @Test
  public void custom_email_validator_test() throws Exception {
    ValidatorTestController.TestReq testReq = new ValidatorTestController.TestReq("");
    String req = objectMapper.writeValueAsString(testReq);
    mockMvc.perform(MockMvcRequestBuilders.post("/validator")
        .content(req)
        .contentType(MediaType.APPLICATION_JSON_VALUE))
      .andExpect(MockMvcResultMatchers.status().isBadRequest())
      .andExpect(MockMvcResultMatchers.jsonPath("$.email", Is.is("is not null!!")));
  }
}

custom_email_validator

This post is licensed under CC BY 4.0 by the author.