Класи з анотацією @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.