https://www.baeldung.com/global-error-handler-in-a-spring-rest-api 번역
Spring 5, Spring Boot 2 버전
1. Overview
튜토리얼에서는 어떻게 Spring Rest API를 위한 global error handler를 구현할 수 있는지 알아보자.
문제를 쉽게 진단할 수 있도록 모든 정보를 해당 클라이언트에 제공한다는 분명한 목표와 함께 각 예외의 의미 체계를 사용하여 클라이언트에 의미 있는 오류 메시지를 작성합니다.
2. A Custom Error Message
wire를 통해 error를 보내는 간단한 구조를 구현해보자.
public class ApiError {
private HttpStatus status;
private String message;
private List<String> errors;
public ApiError(HttpStatus status, String message, List<String> errors) {
super();
this.status = status;
this.message = message;
this.errors = errors;
}
public ApiError(HttpStatus status, String message, String error) {
super();
this.status = status;
this.message = message;
errors = Arrays.asList(error);
}
}
여기에 있는 정보는 간단해야한다.
status - HTTP 상태 코드
message - exception과 관련된 error message
error - 생성된 에러 메세지 목록
실제 예외 처리 로직의 경우 @ControllerAdvice 애노테이션을 사용한다.
@ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
...
}
3. Handling Bad Request Exceptions
가장 많이 발생하는 공통 에러를 어떻게 처리할 수 있는지 보자.
클라이언트에게 보내는 유효하지 않은 API의 시나리오:
BindException - fatal binding error 일때 발생한다
MethodArgumentNotVaildException - @Vaild 애노테이션에서 실패했을때 발생한다.
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
List<String> errors = new ArrayList<String>();
for (FieldError error : ex.getBindingResult().getFieldErrors()) {
errors.add(error.getField() + ": " + error.getDefaultMessage());
}
for (ObjectError error : ex.getBindingResult().getGlobalErrors()) {
errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
}
ApiError apiError =
new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
return handleExceptionInternal(
ex, apiError, headers, apiError.getStatus(), request);
}
우리는 ResponseEntityExceptionHandler를 overriding하고 custom하여 구현했다.
항상 이런 케이스는 아니다. 때때로 우리는 기본 구현이 없는 custom exception을 다룰 필요가 있다.
MissingServletRequestPartException : multipart request의 부분을 찾지 못할때 나타남
MissingServletRequestParameterException : 요청에 parameter가 없을때
ConstraintViolationException : 제약 조건을 위배했을때
TypeMismatchException : 잘못된 유형으로 빈 속성을 설정하려고 할 때 발생
MethodArgumentTypeMismatchException : 메서드 인수가 예상 유형이 아닐때 발생
@ExceptionHandler({ MethodArgumentTypeMismatchException.class })
public ResponseEntity<Object> handleMethodArgumentTypeMismatch(
MethodArgumentTypeMismatchException ex, WebRequest request) {
String error =
ex.getName() + " should be of type " + ex.getRequiredType().getName();
ApiError apiError =
new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
return new ResponseEntity<Object>(
apiError, new HttpHeaders(), apiError.getStatus());
}
3.2 Client에서 API 사용 ( Consuming the API From the Client)
MethodArgumentTypeMismatchException 를 발생시키는 테스트를 보자
Long 타입이 아닌 String 타입의 id 를 요청으로 보낸다.
@Test
public void whenMethodArgumentMismatch_thenBadRequest() {
Response response = givenAuth().get(URL_PREFIX + "/api/foos/ccc");
ApiError error = response.as(ApiError.class);
assertEquals(HttpStatus.BAD_REQUEST, error.getStatus());
assertEquals(1, error.getErrors().size());
assertTrue(error.getErrors().get(0).contains("should be of type"));
}
다음과 같은 요청을 보낸다.
Request method: GET
Request path: http://localhost:8080/spring-security-rest/api/foos/ccc
JSON error는 이렇게 나타난다.
{
"status": "BAD_REQUEST",
"message":
"Failed to convert value of type [java.lang.String]
to required type [java.lang.Long]; nested exception
is java.lang.NumberFormatException: For input string: \"ccc\"",
"errors": [
"id should be of type java.lang.Long"
]
}
'JAVA' 카테고리의 다른 글
print.out 으로 File 입출력하기 (0) | 2022.10.04 |
---|---|
Window / Java 버전 여러개 사용하기 (0) | 2022.05.04 |
Maven BOM (0) | 2021.12.06 |
@Resource vs @Autowired (0) | 2021.11.11 |
Java Map Clear vs Null vs New (0) | 2021.11.09 |