@Valid
RestController를 이용해 @RequestBody 객체를 사용자로부터 가져올 때, 데이터가 유효한지(ex) 누락, 최대 크기 초과 등) 검증할 수 있다. (@Valid
또는 @Validated
어노테이션)
implementation 'org.springframework.boot:spring-boot-starter-validation'
TEST API를 작성해보자
@PostMapping("/user")
public ResponseEntity<ResponseDto> signIn(@Valid @RequestBody SignInDto SignInDto){
// ...
}
파라미터에 @RequestBody와 함께 @Valid를 사용하면, RequestBody로 들어오는 객체에 대한 검증을 수행한다.
검증의 세부적인 사항은 객체 안에 정의해둬야 한다.
@AllArgsConstructor
@Getter
public class SignInDto {
@NotBlank
@Email(message = NOT_VALID_EMAIL)
private String email;
@NotBlank
private String password;
}
@Min(value = 2, message = NOT_VALID_STATE)
@Max(value = 4, message = NOT_VALID_STATE)
private int state;
state가 2보다 작거나 4보다 크면 NOT_VALID_STATE(상수) 메세지가 응답되도록 한다.
응답 예시
{
"message": " Invalid Input Value",
"status": 400,
"errors": [
{
"field": "bucketState",
"value": "1",
"reason": "유효한 버킷 state가 아닙니다."
}
],
"code": "C001"
}
위에서 작성한 것처럼 message 를 이용해 응답 메세지를 설정할 수 있다.
따로 설정하지 않으면 default message가 응답된다.
ex) Email : 올바른 형식의 이메일 주소여야 합니다
/**
* javax.validation.Valid or @Validated 으로 binding error 발생 시 발생
* HttpMessageConverter 에서 등록한 HttpMessageConverter binding 못할 경우 발생
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("MethodArgumentNotValidException : " + e.getMessage());
final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE, e.getBindingResult());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
class SignInDtoTest {
private static ValidatorFactory factory;
private static Validator validator;
@BeforeAll
public static void init() {
factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@AfterAll
public static void close() {
factory.close();
}
@DisplayName("email에 빈문자열 전송 시 에러 발생")
@Test
void 빈문자열_유효성_실패_테스트() {
// given
SignInDto signInDto = new SignInDto("", "test");
// when
Set<ConstraintViolation<SignInDto>> violations = validator.validate(signInDto); // 유효하지 않은 경우 violations 값을 가지고 있다.
// then
assertThat(violations).isNotEmpty();
violations
.forEach(error -> {
assertThat(error.getMessage()).isEqualTo("공백일 수 없습니다");
});
}
@DisplayName("이메일 형식 아닌 경우 에러 발생")
@Test
void 이메일_형식_유효성_실패_테스트() {
// given
SignInDto signInDto = new SignInDto("test", "test");
// when
Set<ConstraintViolation<SignInDto>> violations = validator.validate(signInDto);
// then
assertThat(violations).isNotEmpty();
violations
.forEach(error -> {
assertThat(error.getMessage()).isEqualTo(NOT_VALID_EMAIL);
});
}
@DisplayName("비밀번호 빈문자열인 경우 에러 발생")
@Test
void 비밀번호_유효성_실패_테스트() {
// given
SignInDto signInDto = new SignInDto("test@gmail.com", "");
// when
Set<ConstraintViolation<SignInDto>> violations = validator.validate(signInDto);
// then
assertThat(violations).isNotEmpty();
violations
.forEach(error -> {
assertThat(error.getMessage()).isEqualTo("공백일 수 없습니다");
});
}
@DisplayName("유효성 성공")
@Test
void 유효성_성공_테스트() {
// given
SignInDto signInDto = new SignInDto("test@gmail.com", "test");
// when
Set<ConstraintViolation<SignInDto>> violations = validator.validate(signInDto);
// then
assertThat(violations).isEmpty(); // 유효한 경우
}
}
@RunWith(SpringRunner.class)
@WebMvcTest(controllers = TestController.class)
public class TestControllerTest{
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
@Test // NotNull 테스트(bad request)
public void isValidName() throws Exception {
UserRequestDto user = UserRequestDto.builder()
.email("ayong703@gmail.com")
.build();
String userRequestDtoJsonString = objectMapper.writeValueAsString(user);
mvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON)
.content(userRequestDtoJsonString)) // Request Body
.andExpect(status().isBadRequest());
}
@Test // Email 테스트(bad request)
public void isValidEmail() throws Exception {
UserRequestDto user = UserRequestDto.builder()
.name("문아영")
.email("ayong703@")
.build();
String userRequestDtoJsonString = objectMapper.writeValueAsString(user);
mvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON)
.content(userRequestDtoJsonString))
.andExpect(status().isBadRequest());
}
@Test // 성공 테스트
public void isValidData() throws Exception {
UserRequestDto user = UserRequestDto.builder()
.name("문아영")
.email("ayong703@gmail.com")
.build();
String userRequestDtoJsonString = objectMapper.writeValueAsString(user);
mvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON)
.content(userRequestDtoJsonString))
.andExpect(status().isOk());
}
}