JAVA

Custom Error Message Handling for REST API

k9e4h 2022. 1. 13. 10:31

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