Обработка исключений Master Spring

Spring

公众号

предисловие

На этот раз мы узнаем об обработке исключений в Spring.Как веб-программа с Spring в качестве базовой структуры, если исключения в программе не обрабатываются должным образом, например, не делают информацию об исключении удобной, записывают журналы исключений и т. д., информация об исключении возвращается непосредственно клиенту. Отображение его пользователю плохо влияет на взаимодействие с пользователем. Поэтому в этой статье в основном обсуждаются несколько способов реализации унифицированной обработки исключений с помощью Spring, более элегантного захвата информации об исключении, возникающей в программе, и соответствующего ответа клиенту.

Основное содержание этой статьи следующее:

  • HandlerExceptionResolverрасширять

  • @ExceptionHandlerа также@ControllerAdviceиспользовать

  • ResponseEntityExceptionHandlerрасширять

  • ResponseStatusExceptionиспользовать

  • Spring Boot ErrorControllerрасширять

Пример проекта:

Экологическая поддержка:

  • JDK 8

  • SpringBoot 2.1.4

  • Maven 3.6.0

текст

В среде Spring есть много способов обработки исключений, но до Spring 3.2 было два основных способа:HandlerExceptionResolverи используя аннотации@ExceptionHandler, Spring 3.2 предоставляет более богатый метод обработки.

Расширение HandlerExceptionResolver

HandlerExceptionResolverЭто интерфейс для обработки исключений в веб-программах.Методы интерфейса следующие:

@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

тип возвращаемого значенияModelAndViewВидно, что этот интерфейс относится к фреймворку Spring MVC.Реализуя этот метод, захваченное исключение можно разобрать и обработать, а затем вернуть в соответствии с собственными потребностями.ModelAndViewОбъект, который отвечает на запросы клиентов в виде данных или страниц JSON.

Первый взгляд наHandlerExceptionResolverИерархия классов Spring предоставляет 4 класса реализации, которые кратко описаны ниже.

HandlerExceptionResolver 类体系

HandlerExceptionResolver описывать
SimpleMappingExceptionResolver Сопоставьте класс исключения с указанным представлением, обычно используемым для отображения страницы ошибки при возникновении исключения.
DefaultHandlerExceptionResolver HandlerExceptionResolverРеализация по умолчанию, которая обрабатывает исключения Spring MVC.
ResponseStatusExceptionResolver справляться@ResponseStatusАннотированные исключения, преобразуйте соответствующее значение в аннотации в код состояния HTTP, который обычно размещается в пользовательском классе исключений.
ExceptionHandlerExceptionResolver справляться@ExceptionHandlerАннотированные исключения

Когда нам нужно реализовать пользовательскийHandlerExceptionResolverдо тех пор, пока абстрактный класс наследует от негоAbstractHandlerExceptionResolver, приоритетdoResolveExceptionметод подойдет.

Пример кода ниже обрабатывает то, что происходит в программеIllegalArgumentExceptionисключение, и поMappingJackson2JsonViewobject возвращает объект данных JSON клиенту. если неIllegalArgumentExceptionисключение, возвратnullУказывает, что будут обработаны другие обработчики исключений.Из-за механизма цепочки обработки исключений, если исключение не обработано, исключение будет возвращено клиенту веб-контейнером.

@Component
public class RestResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver {

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        try {
            if (ex instanceof IllegalArgumentException) {
                ModelAndView modelAndView = new ModelAndView();
                Map<String, String> maps = new HashMap<>();
                maps.put("code", "400");
                maps.put("message", ex.getClass().getName());
                maps.put("data", null);
                MappingJackson2JsonView mappingJackson2JsonView = new MappingJackson2JsonView();
                mappingJackson2JsonView.setAttributesMap(maps);
                modelAndView.setView(mappingJackson2JsonView);
                return modelAndView;
            }
        } catch (Exception handlerException) {
            logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
        }
        return null;
    }

}

Мы используем инструмент Postman для имитации API-интерфейса проекта запроса./exception1Чтобы вызвать аномальный триггер, обычно вы можете увидеть следующие эффекты:

image-20190518131151510

@ExceptionHandler

Далее мы смотрим на@ExceptionHandlerИспользование этой аннотации обычно определяется в методе контроллера, указывая, что указанное исключение, возникающее в контроллере, обрабатывается, как показано в следующем коде:

@RestController
public class RestApiController {
    //...

    @ExceptionHandler({IllegalStateException.class})
    public ModelAndView handleIllegalStateException(IllegalStateException ex) {
        System.out.println("非法状态异常出现,需要处理 " + ex.getMessage());
        ModelAndView modelAndView = new ModelAndView();
        Map<String, String> maps = new HashMap<>();
        maps.put("data", null);
        maps.put("message", ex.getClass().getName());
        maps.put("code", "400");
        MappingJackson2JsonView mappingJackson2JsonView = new MappingJackson2JsonView();
        mappingJackson2JsonView.setAttributesMap(maps);
        modelAndView.setView(mappingJackson2JsonView);
        return modelAndView;
    }
}

@ExceptionHandlerВы можете задать несколько типов исключений, которые необходимо захватить и обработать, или оставить это поле пустым и использовать по умолчанию все типы исключений.mvc-ann-exceptionhandler

Затем используйте инструмент Postman для имитации API-интерфейса проекта запроса./exception2Чтобы вызвать исключение, посмотрите данные ответа:

image-20190518134744575

использовать этот путь@ExceptionHandlerЕсть недостаток, то есть он будет обрабатывать исключения только под текущим контроллером.Если вам нужно реализовать обработку исключений глобального контроллера, вам нужно сотрудничать с аннотациями.@ControllerAdviceПри совместном использовании этот метод обработки будет представлен далее.

@ControllerAdvice

Spring 3.2 представляет новую аннотацию@ControllerAdvice, который используется для обработки исключений во всех контроллерах в одном месте, и указать класс в качестве глобального обработчика исключений, используя@ExceptionHandlerАннотируйте метод для обработки исключений. Конкретный пример кода выглядит следующим образом:

@ControllerAdvice
public class NormalExceptionHandler {
    @ExceptionHandler()
    public ResponseEntity handleException(Exception e) {
        System.out.println("NormalExceptionHandler handle exception");
        return ResponseEntity.ok(new Result<>(400, e.getMessage(), null));
    }
}

Объект Result в коде — это просто объект передачи данных (DTO), который удобен для возврата данных в едином формате клиенту.

Давайте рассмотрим использование инструмента Postman для имитации API-интерфейса запросов./exception3Данные ответа показаны на рисунке ниже.

image-20190518144403940

еще одно замечание@RestControllerAdviceа также@ControllerAdviceочень похоже, на самом деле@ControllerAdviceа также@ResponseBodyЭффект комбинации аннотаций заключается в том, что объект, возвращаемый методом обработки исключений, будет сериализован непосредственно в данные JSON для клиента.Использование выглядит следующим образом:

@RestControllerAdvice
public class RestExceptionHandler {
    @ExceptionHandler({ArithmeticException.class})
    public Result handlerException(Exception e) {
        return new Result<>(400, e.getMessage(), null);
    }
}

Эта аннотация была введена в Spring 4.3, в основном для облегчения прямого возврата данных в формате JSON для исключений запроса REST без использованияResponseEntityОбъекты для передачи данных.

@ControllerAdviceПо умолчанию перехватываются все исключения, возникающие в контроллере.Конечно, область действия также может быть ограничена.Ограниченные методы включают в себя ограниченные аннотации, имена пакетов и т. д. Конкретные примеры следующие:

// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}

// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}

для глобального@ExceptionHandlerОписание метода обработки, официальная документация имеет дополнительные примечания следующего содержания:

Global @ExceptionHandler methods (from a @ControllerAdvice) are applied after local ones (from the @Controller).

Это показывает, что обработка исключений также имеет приоритет и сначала передается контроллеру в текущем контроллере.@ExceptionHandlerМетод обработан, если не обработан, то будет обработан глобальным@ExceptionHandlerобработка методом.

Расширение ResponseEntityExceptionHandler

ResponseEntityExceptionHandlerКласс является классом обработки в основном для исключений, создаваемых Spring MVC, таких как 405 запросов, 400 запросов и т. д. по умолчанию.ResponseEntityExceptionHandlerProcessing, мы можем переопределить его методы, унаследовав этот класс, чтобы реализовать обработку конкретных исключений запроса. Например, следующий код реализует обработку ответов на исключения запросов 405.

@@ControllerAdvice
public class CustomWebResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
    @Override
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        switch (status) {
            case METHOD_NOT_ALLOWED:
                return getMethodNotAllowedResponse(request);
            default:
                return ResponseEntity.ok(new Result<>(status.value(), status.getReasonPhrase(), null));
        }
    }

    public ResponseEntity getMethodNotAllowedResponse(WebRequest request) {
        String uri = "";
        if (request instanceof ServletWebRequest) {
            uri = ((ServletWebRequest) request).getRequest().getRequestURI();
        }
        Result<Object> result = new Result<>();
        result.setCode(HttpStatus.METHOD_NOT_ALLOWED.value());
        result.setMessage(uri + " 请求方式不正确");
        return ResponseEntity.ok(result);
    }
}

Таким образом мы пытаемся отправить GET-запрос на API-интерфейс./hello, будет возвращена следующая информация:

image-20190518162624412

тогдаResponseEntityExceptionHandlerСуществуют также ограничения.В настоящее время поддерживаются стандартные исключения SpringMVC только для следующих 15 типов исключений:

  • HttpRequestMethodNotSupportedException

  • HttpMediaTypeNotSupportedException

  • HttpMediaTypeNotAcceptableException

  • MissingPathVariableException

  • MissingServletRequestParameterException

  • ServletRequestBindingException

  • ConversionNotSupportedException

  • TypeMismatchException

  • HttpMessageNotReadableException

  • HttpMessageNotWritableException

  • MethodArgumentNotValidException

  • MissingServletRequestPartException

  • BindException

  • NoHandlerFoundException

  • AsyncRequestTimeoutException

ResponseStatusException

ResponseStatusExceptionЭтот класс был представлен в Spring 5.0 и связывает код состояния HTTP и необязательную причину. Мы можем напрямую создать этот объект исключения в методе запроса и вернуть его. Его очень просто использовать:

@GetMapping("/exception4")
public ResponseEntity<String> exception4(String param) {
    throw new ResponseStatusException(HttpStatus.NOT_FOUND, "资源未找到");
}

Хотя этот метод может напрямую возвращать код ответа и конкретную причину, он не обеспечивает единообразной обработки исключений.@ControllerAdviceиспользуется в комбинации.

Spring Boot ErrorController

ErrorControllerЭто интерфейс, представленный Spring Boot 2.0, и класс реализации, основанный на нем.org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorControllerПредоставляет нам общий способ обработки ошибок. Ниже приведены ключевые методы этого класса реализации:

@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
        HttpServletResponse response) {
    HttpStatus status = getStatus(request);
    Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
            request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
    response.setStatus(status.value());
    ModelAndView modelAndView = resolveErrorView(request, response, status, model);
    return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}

@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    Map<String, Object> body = getErrorAttributes(request,
            isIncludeStackTrace(request, MediaType.ALL));
    HttpStatus status = getStatus(request);
    return new ResponseEntity<>(body, status);
}

Из этих двух методов видно, что для плохих запросовBasicErrorControllerОн обеспечивает возврат в двух формах данных: одна — HTML-страница, а другая — данные JSON; если мы напрямую используем браузер для доступа к интерфейсу, мы видим следующее:errorHtmlРазница между данными HTML-страницы, возвращаемыми методом, — это разница в значении Accept в заголовке во время запроса.

image-20190518170154527

Кроме того, Spring Boot обеспечивает унифицированную обработку информации об ошибках, которую разрешено закрывать, если она находится в файле конфигурации.application.propertiesнастраиватьserver.error.whitelabel.enabledдляfalseВот и все.

server.error.whitelabel.enabled=false

Конечно, мы также можем расширяться на основе этого, например, реализовать собственный контроллер ошибок, наследованиеBasicErrorController, Напишите собственную логику отображения ошибок и содержание, например следующий код:

@Component
public class CustomErrorController extends BasicErrorController {

    public CustomErrorController(ErrorAttributes errorAttributes) {
        super(errorAttributes, new ErrorProperties());
    }

    @RequestMapping(produces = MediaType.APPLICATION_XML_VALUE)
    public ResponseEntity<Map<String, Object>> xmlError(HttpServletRequest request, HttpStatus status) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", status.value());
        map.put("message", status.getReasonPhrase());
        return ResponseEntity.ok(map);
    }
}

осуществленныйCustomErrorControllerПринять запросapplication/xmlВсе возникающие исключения возвращаются в формате XML, как показано на рисунке:

image-20190518171944860

Примечание. Spring Boot по умолчанию не поддерживает преобразование данных в формате XML, и в файл POM необходимо добавить дополнительные библиотеки зависимостей:

<dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

Эпилог

В этой статье мы в основном мы изучаем пять методов обработки исключений на весенние рамочные рамки и общее поведение ботинок для обработки исключений. Существуют различные формы, но определенные ситуации должны быть настроены. Для того, чтобы обеспечить надежность программы и облегчить Быстрое позиционирование ненормальных проблем по запросам, мы должны предоставить единое метод обработки исключений для программы и использовать его в обычных проектах.

Если вы чувствуете, что получили что-то после прочтения, нажмите [Хорошо выглядит], нажмите на изображение заголовка статьи и отсканируйте код, чтобы подписаться на [технический блог Венрена] 😄😄😄.

Ссылаться на