Exception 처리를 통해 HTTP Response 처리
Exception 처리를 통해 404나 400으로 HTTP 통신을 해보자
Exception은 @valid
나 지난 포스팅한 글인 커스텀한 애노테이션을 통해 파라미터 검증하기를 통해 일어난 MethodArgumentNotValidException
이라고 가정
MethodArgumentNotValidException
아니더라도 모든 Exception에 적용이 가능하다
1. 들어오는 파라미터
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MemberRequest {
@NullCheck(message = "패스워드를 입력해야합니다~") private String memberPw;
@NullCheck(message = "아이디를 입력해야합니다~") private String memberId;
@NullCheck(message = "이름을 입력해야합니다~") private String memberName;
@NullCheck(message = "수량은 0보다 커야합니다~", type = "Number") private String orderCount;
@NotNull private String phoneNumber;
...
}
- 전 포스팅에서 만든
@NullCheck
나@NotNull
을 통해 파라미터 검증이 실패하면MethodArgumentNotValidException
이 터지게 된다

이렇게 터진 Exception을 ExceptionHandler로 처리해보자
-
- Response Body부분을 담당할 ErrorResponse 클래스를 만들어놓고
-
- ExceptionHandler를 통해 Exception에 있는 에러데이터를 사용하여 ErrorResponse에 담아두자
-
- 그 뒤, ResponseEntity에 ErrorResponse를 담아서 반환한다
2. 필요한 클래스
2.1 ErrorResponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter
public class ErrorResponse {
private final String status_code;
private final String status_msg;
private List<ValidationTuple> validation;
@Builder
public ErrorResponse(String code, String message, List<ValidationTuple> validation) {
this.code = code;
this.message = message;
this.validation = validation != null ? validation: new ArrayList<ValidationTuple>();
}
}
2.2 ValidationTuple
1
2
3
4
5
6
@RequiredArgsConstructor
@Getter
public class ValidationTuple {
private final String fieldName;
private final String errorMessage;
}
@RequiredArgsConstructor
을 안 쓰고 생성자를 써도 된다@RequiredArgsConstructor
는 생성자가 하나만 있어도 될경우 알아서 모든필드인자를 갖는 생성자를 만들어준다
2.3 ControllerAdvice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ErrorResponse invalidRequestHandler(MethodArgumentNotValidException e) {
List<ValidationTuple> errorList = e.getFieldErrors().stream()
.map(x -> new ValidationTuple(x.getField(), x.getDefaultMessage()))
.collect(Collectors.toList());
//만약 e.getFieldErrors()가 안 뜬다면?
// List<ValidationTuple> errorFields = e.getBindingResult().getFieldErrors().stream()
// .map(x -> new ValidationTuple(x.getField(), x.getDefaultMessage()))
// .collect(Collectors.toList());
ErrorResponse response = ErrorResponse.builder()
.status_code("400")
.status_msg("잘못된 요청입니다.")
.validation(errorList)
.build();
return response;
}
MethodArgumentNotValidException
가BindException
을 상속받은 것이 아니라Exception
을 상속받는 것이면 주석을 사용한다- 1번째라인을 보면
BAD_REQUEST(=400)
을 Response한다고 지정하였다
3. 결과
{
"status_code": "400",
"status_msg": "잘못된 요청입니다.",
"paramValid": [
{
"fieldName": "memberId",
"errorMessage": "아이디를 입력해야합니다~"
},
{
"fieldName": "orderCount",
"errorMessage": "수량은 0보다 커야합니다~"
}
]
}
cf. 테스트코드 - JUNIT5
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
43
44
45
46
47
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.Commit;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest
@AutoConfigureMockMvc
@Transactional
class ControllerTest {
@Autowired private MockMvc mockMvc;
@Autowired private ObjectMapper objectMapper;
@Test @DisplayName("파라미터오류") void createNoParam() throws Exception {
MemberRequest request = new MemberRequest();
//request.setMemberId("em123412");
//request.setOrderCount("0");
request.setMemberPw("12341234");
request.setMemberName("김찬숙");
request.setPhoneNumber("01091921312")
String json = objectMapper.writeValueAsString(request);
mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/barcodes")
.contentType(MediaType.APPLICATION_JSON) //기본값
.content(json))
.andExpect(MockMvcResultMatchers.jsonPath("$.status_msg").value("잘못된 요청입니다."))
.andExpect(MockMvcResultMatchers.jsonPath("$.status_code").value("400"))
.andDo(MockMvcResultHandlers.print());
}
결과
MockHttpServletResponse:
Status = 400
Error message = null
Headers = [Content-Type:"text/plain;charset=UTF-8", Content-Length:"279"]
Content type = text/plain;charset=UTF-8
Body = {"status_code":"400","status_msg":"잘못된 요청입니다.","validation":[{"fieldName":"memberId","errorMessage":"아이디를 입력해야합니다~"},{"fieldName":"orderCount","errorMessage":"수량은 0보다 커야합니다~"}]}
Forwarded URL = null
Redirected URL = null
Cookies = []
Spring
👉 Spring Security - Role (단일 권한, 복합 권한)
2022.11.10
👉 Spring Security를 이용한 로그인 처리
2022.10.31
👉 REST API - Exception을 통하여 HTTP Response 처리
2022.10.10
👉 Assert을 커스텀(custom)하여 파라미터 검증
2022.10.10
👉 애노테이션을 사용한 파라미터 검증
2022.10.09
👉 제네릭 기본 및 응용
2022.10.07
👉 DataSource와 ConnectionPool
2022.07.11
👉 JSP - EL 표현식에서 언제 오류 페이지가 나타날까
2022.05.27
👉 IoC와 DI컨테이너
2022.05.26
👉 DI(의존성 주입) 생성자 주입는 왜 필요한가
2022.05.25
👉 스프링을 왜 사용하는가
2022.05.23
👉 Servlet
2022.02.07
Java
👉 Spring Security - Role (단일 권한, 복합 권한)
2022.11.10
👉 Spring Security를 이용한 로그인 처리
2022.10.31
👉 REST API - Exception을 통하여 HTTP Response 처리
2022.10.10
👉 Assert을 커스텀(custom)하여 파라미터 검증
2022.10.10
👉 애노테이션을 사용한 파라미터 검증
2022.10.09
👉 제네릭 기본 및 응용
2022.10.07
👉 Refactoring
2022.08.16
👉 JSP - EL 표현식에서 언제 오류 페이지가 나타날까
2022.05.27
👉 IoC와 DI컨테이너
2022.05.26
👉 DI(의존성 주입) 생성자 주입는 왜 필요한가
2022.05.25
👉 스프링을 왜 사용하는가
2022.05.23
👉 Servlet
2022.02.07
Rest API
👉 REST API - Exception을 통하여 HTTP Response 처리
2022.10.10
댓글 쓰기