ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] 스프링 REST서버에서 에러를 처리하는 방법
    Spring 2019. 6. 3. 09:24

    스프링 3.2 이전 스프링 MVC 애플리케이션에서 예외를 처리하는 방법 2가지

    • @ExceptionHandler
    • HandlerExceptionResolver

    스프링 3.2 이후 @ControllerAdvice 어노테이션은 위의 2가지 방법의 단점을 보완하여 전체 애플리케이션의 예외를 한 곳에서 처리할 수 있는 방법을 제공한다.

     

    스프링 5에서는 ResponseStatusException 클래스를 사용하여 REST API의 에러를 쉽게 처리할 수 있는 방법을 제공한다.

    방법 1 – 컨트롤러에 @ExceptionHandler 사용하기

    • @Controller 레벨에 @ExceptionHandler 어노테이션과 함께 예외를 처리할 메소드를 정의
    public class FooController{
         
        //...
        @ExceptionHandler({ CustomException1.class, CustomException2.class })
        public void handleException() {
            //
        }
    }
    • 단점: @ExceptionHandler 어노테이션은 전체 애플리케이션에 적용되는 것이 아닌 특정 컨트롤러에만 적용

    방법2 – HandlerExceptionResolver

    The second solution is to define an HandlerExceptionResolver – this will resolve any exception thrown by the application. It will also allow us to implement a uniform exception handling mechanism in our REST API.

    2.1. ExceptionHandlerExceptionResolver

    This resolver was introduced in Spring 3.1 and is enabled by default in the DispatcherServlet. This is actually the core component of how the @ExceptionHandler mechanism presented earlier works.

    2. DefaultHandlerExceptionResolver

     

    3. ResponseStatusExceptionResolver

    This resolver was also introduced in Spring 3.0 and is enabled by default in the DispatcherServlet. Its main responsibility is to use the @ResponseStatus annotation available on custom exceptions and to map these exceptions to HTTP status codes.

    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    public class ResourceNotFoundException extends RuntimeException {
        public ResourceNotFoundException() {
            super();
        }
        public ResourceNotFoundException(String message, Throwable cause) {
            super(message, cause);
        }
        public ResourceNotFoundException(String message) {
            super(message);
        }
        public ResourceNotFoundException(Throwable cause) {
            super(cause);
        }
    }

    Same as the DefaultHandlerExceptionResolver, this resolver is limited in the way it deals with the body of the response – it does map the Status Code on the response, but the body is still null.

    4. SimpleMappingExceptionResolver and AnnotationMethodHandlerExceptionResolver

    The SimpleMappingExceptionResolver has been around for quite some time – it comes out of the older Spring MVC model and is not very relevant for a REST Service.

    5. Custom HandlerExceptionResolver

    @Component
    public class RestResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver {
     
        @Override
        protected ModelAndView doResolveException
          (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
            try {
                if (ex instanceof IllegalArgumentException) {
                    return handleIllegalArgument((IllegalArgumentException) ex, response, handler);
                }
                ...
            } catch (Exception handlerException) {
                logger.warn("Handling of [" + ex.getClass().getName() + "] 
                  resulted in Exception", handlerException);
            }
            return null;
        }
     
        private ModelAndView handleIllegalArgument
          (IllegalArgumentException ex, HttpServletResponse response) throws IOException {
            response.sendError(HttpServletResponse.SC_CONFLICT);
            String accept = request.getHeader(HttpHeaders.ACCEPT);
            ...
            return new ModelAndView();
        }
    }

    we return a ModelAndView – this is the body of the response and it will allow us to set whatever is necessary on it.

    방법 3 – @ControllerAdvice

     

    @ControllerAdvice
    public class RestResponseEntityExceptionHandler 
      extends ResponseEntityExceptionHandler {
     
        @ExceptionHandler(value 
          = { IllegalArgumentException.class, IllegalStateException.class })
        protected ResponseEntity<Object> handleConflict(
          RuntimeException ex, WebRequest request) {
            String bodyOfResponse = "This should be application specific";
            return handleExceptionInternal(ex, bodyOfResponse, 
              new HttpHeaders(), HttpStatus.CONFLICT, request);
        }
    }

    The@ControllerAdvice annotation allows us to consolidate our multiple, scattered@ExceptionHandlers from before into a single, global error handling component.

    The actual mechanism is extremely simple but also very flexible. It gives us:

    • Full control over the body of the response as well as the status code
    • Mapping of several exceptions to the same method, to be handled together, and
    • It makes good use of the newer RESTful ResposeEntity response

    One thing to keep in mind here is to match the exceptions declared with @ExceptionHandler with the exception used as the argument of the method. If these don’t match, the compiler will not complain – no reason it should, and Spring will not complain either.

    방법 4 – ResponseStatusException (Spring 5 and Above)

     

    @GetMapping(value = "/{id}")
    public Foo findById(@PathVariable("id") Long id, HttpServletResponse response) {
        try {
            Foo resourceById = RestPreconditions.checkFound(service.findOne(id));
     
            eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
            return resourceById;
         }
        catch (MyResourceNotFoundException exc) {
             throw new ResponseStatusException(
               HttpStatus.NOT_FOUND, "Foo Not Found", exc);
        }
    }

    What are the benefits of using ResponseStatusException?

    • Excellent for prototyping: We can implement a basic solution quite fast
    • One type, multiple status codes: One exception type can lead to multiple different responses. This reduces tight coupling compared to the @ExceptionHandler
    • We won’t have to create as many custom exception classes
    • More control over exception handlingsince the exceptions can be created programmatically

    And what about the tradeoffs?

    • There’s no unified way of exception handling: It’s more difficult to enforce some application-wide conventions, as opposed to @ControllerAdvice which provides a global approach
    • Code duplication: We may find ourselves replicating code in multiple controllers

    We should also note that it’s possible to combine different approaches within one application.

    For example, we can implement a @ControllerAdvice globally, but also ResponseStatusExceptions locally.


    출처 : https://www.baeldung.com/exception-handling-for-rest-with-spring

    댓글

Designed by Tistory.