- 기존
@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 생성, 및 테스트 코드 작성.
- 1 ]
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")
: 테스트용 handlerTestReq.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!!")));
}
}