[Spring boot + Rest API] Handle Exception

1. Overview

Exception trong spring có thể được xử lý bằng nhiều cách. Trong phạm vi bài này m sẽ giới thiệu đến các bạn một vài cách xử lý tương đối cơ bản

2. ExceptionHandler

Có thể sử dụng annotation này nhằm mục đích đánh dấu method nào sẽ xử lý exception cụ thể nào trong controller VD:

    @GetMapping(path = "/car/{id}")
    public String getCar(@PathVariable("id") int id) {
        switch (id) {
            case 1:
                throw new CarNotFoundException();
            default:
                return "Day la car n";
        }
    }

    @ExceptionHandler({CarNotFoundException.class})
    public void handleCarException(CarNotFoundException e) {
        System.out.println(e.getMessage());        
    }

Như các bạn đã thấy, method handleCarException được sử dụng để xử lý các exception xảy ra trong restapi getCar. Cách sử dụng này có một khuyết điểm là chỉ sử dụng trong ngữ cảnh controller cụ thể, không mang tính global được.

3. HandlerExceptionResolver

Bản thân spring đã có một vài cài đặt của handlerExceptionResolver. Cụ thể:

  • ExceptionHandlerExceptionResolver được setup thông qua annotation @ExceptionHandler
  • ResponseStatusExceptionResolver được setup thông qua annotation @ResponseStatus Ngoài ra spring còn cung cấp một lớp AbstractHandlerExceptionResolver cho phép chung ta tự custom một handlerExceptionResolver của ứng dụng mình
@Component
public class MyHandlerExceptionResolve extends AbstractHandlerExceptionResolver {

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        try {
            if (ex instanceof HomeNotFoundException) {
                // do something with error
            }
        } catch (Exception handlerException) {
        }
        return new ModelAndView("error");
    }
}

4. ControllerAdvice

Giải pháp này đến từ spring AOP. M sẽ có một topic nói về project này. Bạn cứ hiểu nôm na đó là lập trình hướng khía cạnh. Thay vì quan tâm đến việc cụ thẻ hóa các sự vật sự việc trong đời sống như OOP thì AOP quan tâm đến một khía cạnh của vấn đề, Ví dụ: Logging, Handler Exception..... Với các làm này, bạn có thể bốc nguyên logic xử lý exception ra một class riêng.

@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(CarNotFoundException.class)
    protected ResponseEntity<ApiError> handleEntityNotFound(
            CarNotFoundException ex) {
        ApiError apiError = new ApiError(NOT_FOUND);
        apiError.setMessage(ex.getMessage());
        return buildResponseEntity(apiError);
    }

    private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
        return new ResponseEntity<>(apiError, apiError.getStatus());
    }

    @Override
    protected ResponseEntity<Object> handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest webRequest) {
        return super.handleAsyncRequestTimeoutException(ex, headers, status, webRequest); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return super.handleHttpMediaTypeNotAcceptable(ex, headers, status, request); //To change body of generated methods, choose Tools | Templates.
    }

}

Ở đây m có sử dụng đến một class ResponseEntityExceptionHandler. về cơ bản thằng này giống thằng AbstractHandlerExceptionResolver. Đều cung cấp các method cho phép ta xử lý exception. nhưng anh chàng này thay vì trả về ModelAndView thì trả về ResponseEntity. Ngoài những method đã được định nghĩa sẵn, bạn có thể kết hợp với @ExceptionHandler như đã giới thiệu ở phần 1 để xây dựng cho mình cơ chế xử lý exception tiện lợi nhất.