REST API Exception 처리

 

REST API Exception

적절한 HttpStatus로 응답이 필요하다.

 

ResponseStatusException

ResponseStatusException 를 활용하여, 적절한 HttpStatus 와 message를 함께 Throw 시킨다.

	
@PostMapping("/account")
public ResponseEntity<AccountDTO.CommonResponse> registerAccount(
    @RequestBody AccountDTO.RegisterRequest registerRequest) {
    
    // ...
    
    // registerRequest.id가 없거나, 길이가 1이하 일 경우 BAD_REQUEST
    if (registerRequest.getId() == null || registerRequest.getId().length() < 1) {
        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "id 값이 없습니다.");
    }
    
    // ...
    
    AccountDTO.CommonResponse account = accountService.saveAccountIfNotExist(registerRequest);
    
    return new ResponseEntity<>(account, HttpStatus.CREATED);
}

/*
ResponseBody
{
  "timestamp": "2020-12-17T08:19:41.919+0000",
  "status": 400,
  "error": "Bad Request",
  "message": "id 값이 없습니다.",
  "path": "/accounts"
}
*/

 

ExceptionHandler

@ExceptionHandler(Exception.class) 를 활용하여, 해당 @Controller, @RestController 내 에서 발생한 해당 Exception을 감지하여 처리한다.

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ExceptionResponse {
    private Timestamp timestamp;
    
    private Integer status;
    
    private String error;
    
    private String message;
    
    private String path;
    
    public ExceptionResponse(HttpStatus httpStatus, BindingResult bindingResult, String path) {
        this.timestamp = new Timestamp(System.currentTimeMillis());
        this.status = httpStatus.value();
        this.error = httpStatus.name();
        this.message = createErrorMessage(bindingResult);
        this.path = path;
    }
    
    public ExceptionResponse(HttpStatus httpStatus, String reason, String path) {
        this.timestamp = new Timestamp(System.currentTimeMillis());
        this.status = httpStatus.value();
        this.error = httpStatus.name();
        this.message = reason;
        this.path = path;
    }
    
    private static String createErrorMessage(BindingResult bindingResult) {
        StringBuilder stringBuilder = new StringBuilder();
        boolean isFirst = true;
    
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            if (!isFirst) {
                stringBuilder.append(", ");
            } else {
                isFirst = false;
            }
    
            stringBuilder.append("[");
            stringBuilder.append(fieldError.getField());
            stringBuilder.append("] ");
            stringBuilder.append(fieldError.getDefaultMessage());
            stringBuilder.append(" ");
        }
    
        return stringBuilder.toString();
    }
}

 

@PostMapping("/account")
public ResponseEntity<AccountDTO.CommonResponse> registerAccount(
    @RequestBody @Valid AccountDTO.RegisterRequest registerRequest) {
    AccountDTO.CommonResponse account = accountService.saveAccountIfNotExist(registerRequest);
    return new ResponseEntity<>(account, HttpStatus.CREATED);
}

// registerAccount 메소드에서 AccountDTO.RegisterRequest를 @Vaild하는 과정에서 이슈가 생긴 경우
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ExceptionResponse> handleMethodArgumentNotValid(MethodArgumentNotValidException exception,
    HttpServletRequest request) {
    // ExceptionResponse 클래스
    return new ResponseEntity<>(
        new ExceptionResponse(HttpStatus.BAD_REQUEST, exception.getBindingResult(), request.getRequestURI()),
        HttpStatus.BAD_REQUEST);
}

/*
ResponseBody
{
  "timestamp": "2020-12-21T05:09:50.020+0000",
  "status": 400,
  "error": "BAD_REQUEST",
  "message": "[password] 반드시 값이 존재하고 길이 혹은 크기가 0보다 커야 합니다. , [id] 반드시 값이 존재하고 길이 혹은 크기가 0보다 커야 합니다. ",
  "path": "/accounts"
}
*/

 

ControllerAdvice

@ControllerAdvice@ExceptionHandler를 활용하여, @Controller, @RestController 에서 발생한 Exception을 관리 할 수 있다.

@ControllerAdvice
public class ResponseExceptionHandler {

    // @Controller, @RestController에서 MethodArgumentNotValidException 발생 할 경우 처리
    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<ExceptionResponse> handleMethodArgumentNotValid(MethodArgumentNotValidException exception,
        HttpServletRequest request) {
        return new ResponseEntity<>(
            new ExceptionResponse(HttpStatus.BAD_REQUEST, exception.getBindingResult(), request.getRequestURI()),
            HttpStatus.BAD_REQUEST);
    }

}

/*
ResponseBody
{
  "timestamp": "2020-12-21T05:09:50.020+0000",
  "status": 400,
  "error": "BAD_REQUEST",
  "message": "[password] 반드시 값이 존재하고 길이 혹은 크기가 0보다 커야 합니다. , [id] 반드시 값이 존재하고 길이 혹은 크기가 0보다 커야 합니다. ",
  "path": "/accounts"
}
*/

Leave a comment