Классы с аннотацией @Controller
и @ControllerAdvice
могут иметь методы, помеченные аннотацией @ExceptionHandler
, для обработки исключений из методов контроллера, как показано в следующем примере:
@Controller
public class SimpleController {
// ...
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
@Controller
class SimpleController {
// ...
@ExceptionHandler
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
}
Исключение может совпадать с распространяемым высокоуровневым исключением (например, прямо генерируемое IOException
) или с вложенной причиной внутри исключения-обёртки (например, IOException
, обернутое внутри IllegalStateException
). Начиная с 5.3, это может совпадать на произвольных уровнях причин, тогда как ранее принималась во внимание только непосредственная причина.
Для совпадающих типов исключений предпочтительно объявлять целевое исключение в качестве аргумента метода, как показано в предыдущем примере. При совпадении нескольких методов исключения, как правило, предпочтительнее совпадение исключения корня, чем совпадение исключения причины. В частности, ExceptionDepthComparator
используется для сортировки исключений на основе их глубины от типа сгенерированного исключения.
Кроме того, объявление аннотации может сужать типы исключений, которые должны совпадать, как показано в следующем примере:
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
// ...
}
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
Можно даже использовать список конкретных типов исключений с очень общей сигнатурой аргументов, как показано в следующем примере:
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
// ...
}
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handle(ex: Exception): ResponseEntity<String> {
// ...
}
Различие между сопоставлением исключений корня и причины может удивить.
В варианте IOException
, показанном ранее, метод обычно вызывается с использованием фактического экземпляра FileSystemException
или RemoteException
в качестве аргумента, поскольку оба они расширяются от IOException
. Однако, если любое такое совпадающее исключение распространяется внутри исключения-обёртки, которое само является IOException
, то переданный экземпляр исключения является этим исключением-обёрткой.
В варианте handle(Exception)
логика работы еще проще. Её вызов происходит при помощи исключения-обёртки в сценарии обёртывания, при этом фактически соответствующее исключение можно найти через ex.getCause()
в этом случае. Переданное исключение является фактическим экземпляром FileSystemException
или RemoteException
только в том случае, если они генерируются как высокоуровневые исключения.
Обычно рекомендуется давать как можно больше конкретики в сигнатуре аргументов, чтобы снизить вероятность несоответствия между типами исключений корня и причины. Рассмотрим возможность разбить метод с несколькими совпадениями на отдельные методы, аннотированные @ExceptionHandler
, каждый из которых соответствует одному определенному типу исключения через свою сигнатуру.
В конфигурации с несколькими аннотациями @ControllerAdvice
мы рекомендуем объявлять отображения основных исключений корня в аннотации @ControllerAdvice
в соответствующем приоритетном порядке. Хотя сопоставление исключения корня предпочтительнее, чем сопоставление исключения причины, определение происходит среди методов данного контроллера или класса, аннотированного @ControllerAdvice
. Это означает, что совпадение причины для бина, аннотированного @ControllerAdvice
, с более высоким приоритетом предпочтительнее любого совпадения (например, корня) для бина, аннотированного @ControllerAdvice
с более низким приоритетом.
И последнее, но не менее важное: реализация метода, помеченного аннотацией @ExceptionHandler
, может отказаться работать с данным экземпляром исключения, повторно сгенерировав его в исходной форме. Это полезно в тех случаях, когда вас интересуют исключительно совпадения на корневом уровне или совпадения в определенном контексте, который не может быть определен статически. Повторная генерация исключения распространяется по оставшейся цепочке разрешения, как если бы данный метод с аннотацией @ExceptionHandler
не совпал с первым.
Поддержка методов с аннотацией @ExceptionHandler
в Spring MVC построена на уровне DispatcherServlet
, механизме HandlerExceptionResolver.
Аргументы метода
Методы с аннотацией @ExceptionHandler
поддерживают следующие аргументы:
Аргумент метода | Описание |
---|---|
Тип исключения |
Обеспечивает доступ к сгенерированному исключению. |
|
Обеспечивает доступ к методу контроллера, который сгенерировал исключение. |
|
Типизированный доступ к параметрам запроса и атрибутам запроса и сессии без прямого использования Servlet API. |
|
Выберите любой конкретный тип запроса или ответа (например, |
|
Обеспечивает наличие сессии. Как следствие, такой аргумент никогда не является |
|
Текущий аутентифицированный пользователь – возможно, конкретный класс реализации |
|
HTTP-метод запроса. |
|
Текущие региональные настройки запроса, определяемые наиболее конкретным доступным |
|
Часовой пояс, связанный с текущим запросом, определенный |
|
Обеспечивает доступ к сырому телу ответа, как это предусмотрено Servlet API. |
|
Обеспечивает доступ к модели для выдачи ответа на ошибку. Всегда пустой. |
|
Задайте атрибуты, используемые в случае перенаправления - (которые будут добавлены к строке запроса) и flash атрибуты для временного хранения до поступления запроса после перенаправления. |
|
Предназначен для обеспечения доступа к любому атрибуту сессии, в отличие от атрибутов модели, хранящихся в сессии в результате объявления аннотации |
|
Предназначен для обеспечения доступа к атрибутам запроса. |
Возвращаемые значения
Методы с аннотацией @ExceptionHandler
поддерживают следующие возвращаемые значения:
Return value | Description |
---|---|
|
Возвращаемое значение преобразуется через экземпляры |
|
Возвращаемое значение задает, что полный ответ (включая HTTP-заголовки и тело) будет преобразован через экземпляры |
|
Имя представления, которое должно быть распознано с помощью реализаций |
|
Экземпляр |
|
Атрибуты для добавления в неявную модель с именем представления, неявно определенным через |
|
Атрибут, добавляемый к модели с именем представления, неявно определенным через Обратите внимание, что аннотация |
|
Используемые атрибуты представления и модели и, при необходимости, статус ответа. |
|
Считается, что метод с возвращаемым типом Если ни одно из вышеперечисленных значений не верно, возвращаемый тип |
Любое другое возвращаемое значение |
Если возвращаемое значение не соответствует ни одному из вышеперечисленных и не является простым типом (как определено BeanUtils#isSimpleProperty), по умолчанию оно считается атрибутом модели, который должен быть добавлен в модель. Если это простой тип, то он остается неразрешенным. |
Работа с исключениями в REST API
Общим требованием для служб REST является включение информации об ошибке в тело ответа. Spring Framework не автоматически этого не делает, поскольку представление информации об ошибке в теле ответа зависит от конкретного приложения. Однако аннотация @RestController
может использовать методы, помеченные аннотацией @ExceptionHandler
, с возвращаемым значением ResponseEntity
для установки статуса и тела ответа. Такие методы также могут быть объявлены в классах, аннотированных @ControllerAdvice
, чтобы можно было применять их глобально.
Приложения, реализующие глобальную обработку исключений с подробным описанием ошибки в теле ответа, должны учитывать возможность расширения ResponseEntityExceptionHandler
, который обеспечивает обработку исключений, возникающих в Spring MVC, и предоставляет перехватчики для настройки тела ответа. Чтобы воспользоваться этим, создайте подкласс ResponseEntityExceptionHandler
, аннотируйте его при помощи @ControllerAdvice
, переопределите необходимые методы и объявите его как бин Spring.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ