Tratamento de exceções em controladores Spring

imagem



Na prática, geralmente é necessário tratar as exceções de maneira centralizada em um controlador ou mesmo em um aplicativo inteiro. Neste artigo, vamos analisar os principais recursos que o Spring Framework oferece para resolver esse problema e, usando exemplos simples, vamos ver como tudo funciona. Quem está interessado neste tópico - seja bem-vindo!



Inicialmente, antes da Primavera 3.2, as principais formas de exceções identificador em um aplicativo foram HandlerExceptionResolver e @ExceptionHandler anotação . Iremos analisá-los com mais detalhes abaixo, mas eles têm certas desvantagens. A partir da versão 3.2, apareceu a anotação @ControllerAdvice , que remove as limitações das soluções anteriores. E no Spring 5, uma nova classe ResponseStatusException foi adicionada que é muito útil para lidar com erros básicos para APIs REST .



E agora, as primeiras coisas primeiro, vamos lá!



Tratamento de exceções do controlador - @ExceptionHandler



@ExceptionHandler . , , .



:



@RestController
public class Example1Controller {

    @GetMapping(value = "/testExceptionHandler", produces = APPLICATION_JSON_VALUE)
    public Response testExceptionHandler(@RequestParam(required = false, defaultValue = "false") boolean exception)
            throws BusinessException {
        if (exception) {
            throw new BusinessException("BusinessException in testExceptionHandler");
        }
        return new Response("OK");
    }

    @ExceptionHandler(BusinessException.class)
    public Response handleException(BusinessException e) {
        return new Response(e.getMessage());
    }

}


testExceptionHandler, BusinessException, — . , , .



handleException . @ExceptionHandler(BusinessException.class), BusinessException. @ExceptionHandler , : @ExceptionHandler({BusinessException.class, ServiceException.class}).



— 200 JSON . , @ResponseStatus, @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR).



:





:





@ExceptionHandler , . @ExceptionHandler , , , .



HandlerExceptionResolver



HandlerExceptionResolver Spring. HandlerExceptionResolver. , , Spring . :



ExceptionHandlerExceptionResolver @ExceptionHandler, .



DefaultHandlerExceptionResolver — Spring , :



Exception HTTP Status Code
BindException 400 (Bad Request)
ConversionNotSupportedException 500 (Internal Server Error)
HttpMediaTypeNotAcceptableException 406 (Not Acceptable)
HttpMediaTypeNotSupportedException 415 (Unsupported Media Type)
HttpMessageNotReadableException 400 (Bad Request)
HttpMessageNotWritableException 500 (Internal Server Error)
HttpRequestMethodNotSupportedException 405 (Method Not Allowed)
MethodArgumentNotValidException 400 (Bad Request)
MissingServletRequestParameterException 400 (Bad Request)
MissingServletRequestPartException 400 (Bad Request)
NoSuchRequestHandlingMethodException 404 (Not Found)
TypeMismatchException 400 (Bad Request)


, REST API . . ModelAndView, , .



ResponseStatusExceptionResolver @ResponseStatus.



ServiceException:



@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public class ServiceException extends Exception {

    public ServiceException(String message) {
        super(message);
    }

}


ServiceException @ResponseStatus value INTERNAL_SERVER_ERROR, - 500.



:



@RestController
public class Example2Controller {

    @GetMapping(value = "/testResponseStatusExceptionResolver", produces = APPLICATION_JSON_VALUE)
    public Response testResponseStatusExceptionResolver(@RequestParam(required = false, defaultValue = "false") boolean exception)
            throws ServiceException {
        if (exception) {
            throw new ServiceException("ServiceException in testResponseStatusExceptionResolver");
        }
        return new Response("OK");
    }

}


GET- exception=true, 500- :





— . , @ResponseStatus .



HandlerExceptionResolver , - JSON XML . , .



:



@Component
public class CustomExceptionResolver extends AbstractHandlerExceptionResolver {

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView modelAndView = new ModelAndView(new MappingJackson2JsonView());
        if (ex instanceof CustomException) {
            modelAndView.setStatus(HttpStatus.BAD_REQUEST);
            modelAndView.addObject("message", "CustomException was handled");
            return modelAndView;

        }
        modelAndView.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
        modelAndView.addObject("message", "Another exception was handled");
        return modelAndView;
    }

}


, . , : , ModelAndView. JSON, .



-, . . , . , — :



@RestController
public class Example3Controller {

    @GetMapping(value = "/testCustomExceptionResolver", produces = APPLICATION_JSON_VALUE)
    public Response testCustomExceptionResolver(@RequestParam(required = false, defaultValue = "false") boolean exception)
            throws CustomException {
        if (exception) {
            throw new CustomException("CustomException in testCustomExceptionResolver");
        }
        return new Response("OK");
    }

}


:





200 JSON .



@ControllerAdvice



— . Spring 3.2 @ControllerAdvice.



:



@ControllerAdvice
public class DefaultAdvice {

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<Response> handleException(BusinessException e) {
        Response response = new Response(e.getMessage());
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

}


, @ControllerAdvice , .

DefaultAdvice handleException. handleException @ExceptionHandler, , , . BusinessException.



: @ExceptionHandler({BusinessException.class, ServiceException.class}). @ExceptionHandler .

, handleException ResponseEntity Response:



public class Response {

    private String message;

    public Response() {
    }

    public Response(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}


, JSON . message HttpStatus.OK, 200.



:



@RestController
public class Example4Controller {

    @GetMapping(value = "/testDefaultControllerAdvice", produces = APPLICATION_JSON_VALUE)
    public Response testDefaultControllerAdvice(@RequestParam(required = false, defaultValue = "false") boolean exception)
            throws BusinessException {
        if (exception) {
            throw new BusinessException("BusinessException in testDefaultControllerAdvice");
        }
        return new Response("OK");
    }

}


, , JSON 200:





?

! :



@ControllerAdvice(annotations = CustomExceptionHandler.class)
public class CustomAdvice {

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<Response> handleException(BusinessException e) {
        String message = String.format("%s %s", LocalDateTime.now(), e.getMessage());
        Response response = new Response(message);
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

}


@ControllerAdvice(annotations = CustomExceptionHandler.class). CustomAdvice , @CustomExceptionHandler.



@CustomExceptionHandler :



@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomExceptionHandler {
}


:



@RestController
@CustomExceptionHandler
public class Example5Controller {

    @GetMapping(value = "/testCustomControllerAdvice", produces = APPLICATION_JSON_VALUE)
    public Response testCustomControllerAdvice(@RequestParam(required = false, defaultValue = "false") boolean exception)
            throws BusinessException {
        if (exception) {
            throw new BusinessException("BusinessException in testCustomControllerAdvice");
        }
        return new Response("OK");
    }

}


Example5Controller @CustomExceptionHandler, Example4Controller . BusinessException CustomAdvice, DefaultAdvice, .



CustomAdvice — :





. @ControllerAdvice, . .



ResponseStatusException.



ResponseStatusException:



@RestController
public class Example6Controller {

    @GetMapping(value = "/testResponseStatusException", produces = APPLICATION_JSON_VALUE)
    public Response testResponseStatusException(@RequestParam(required = false, defaultValue = "false") boolean exception) {
        if (exception) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "ResponseStatusException in testResponseStatusException");
        }
        return new Response("OK");
    }

}


ResponseStatusException , , . @ResponseStatus — -. , .



:





Resumo : Vimos diferentes maneiras de lidar com exceções, cada uma com suas próprias características. Em um aplicativo grande, você pode encontrar várias abordagens ao mesmo tempo, mas deve ter muito cuidado e tentar não complicar demais a lógica de tratamento de erros. Caso contrário, acontecerá que alguma exceção será tratada no manipulador errado e a resposta será diferente da esperada. Por exemplo, se o aplicativo tiver vários orientadores, ao criar um novo, você precisará certificar-se de que ele não interrompa a ordem existente de tratamento de exceções dos controladores antigos.

Portanto, tenha cuidado e tudo funcionará muito bem!



Link para fontes do artigo




All Articles