Класи з анотацією @Controller та @ControllerAdvice можуть мати методи, позначені анотацією @ExceptionHandler, для обробки винятків із методів контролера, як показано в наступному прикладі:

Java

@Controller
public class SimpleController {
    // ...
    @ExceptionHandler
    public ResponseEntity<String> handle(IOException ex) {
        // ...
    }
}
Kotlin
@Controller
class SimpleController {
    // ...
    @ExceptionHandler
    fun handle(ex: IOException): ResponseEntity<String> {
        // ...
    }
}

Виняток може збігатися з поширюваним високорівневим винятком (наприклад, прямо генерований IOException) або з вкладеною причиною всередині винятку-обгортки (наприклад, IOException, обернене всередині IllegalStateException). Починаючи з 5.3, це може збігатися на довільних рівнях причин, тоді як раніше бралася до уваги тільки безпосередня причина. У разі збігу кількох методів виключення зазвичай кращим є збіг винятку кореня, ніж збіг винятку причини. Зокрема, ExceptionDepthComparator використовується для сортування винятків на основі їх глибини від типу згенерованого винятку:

Java

@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
    // ...
}
Kotlin
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handle(ex: IOException): ResponseEntity<String> {
    // ...
}

Можна навіть використовувати список конкретних типів винятків з дуже загальною сигнатурою аргументів, як показано в наступному прикладі:

Java
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
    // ...
}
Kotlin
@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 підтримують такі аргументи:

Аргумент методу Опис

Тип винятку

Забезпечує доступ до згенерованого винятку.

HandlerMethod

Забезпечує доступ до методу контролера, який згенерував виняток.

WebRequest, NativeWebRequest

Типізований доступ до параметрів запиту та атрибутів запиту та сесії без прямого використання Servlet API.

javax. servlet.ServletRequest, javax.servlet.ServletResponse

Обери будь-який тип запиту або відповіді (наприклад, ServletRequest або HttpServletRequest, або MultipartRequest або MultipartHttpServletRequest з Spring).

javax.servlet.http.HttpSession

Забезпечує наявність сесії. Як наслідок, такий аргумент ніколи не є null.
Зверни увагу, що доступ до сесії не є безпечним. Розглянь можливість встановлення прапора synchronizeOnSession екземпляра RequestMappingHandlerAdapter у true, якщо кільком запитам дозволено одночасний доступ до сесії.

java.security.Principal

Поточний автентифікований користувач — можливо, конкретний клас реалізації Principal, якщо він відомий.

HttpMethod

HTTP-метод запиту.

java.util.Locale

Поточні регіональні налаштування запиту, що визначаються найбільш конкретним доступним LocaleResolver — по суті, налаштованим LocaleResolver або LocaleContextResolver.

java.util.TimeZone, java.time.ZoneId

Часовий пояс, пов'язаний з поточним запитом, визначений LocaleContextResolver.

java.io.OutputStream, java.io.Writer

Забезпечує доступ до сирого тіла відповіді, як це передбачено Servlet API.

java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap

Забезпечує доступ до моделі для надання відповіді на помилку. Завжди порожній.

RedirectAttributes

Вкажи атрибути, що використовуються у разі перенаправлення — (які будуть додані до рядка запиту) та flash атрибути для тимчасового зберігання до надходження запиту після перенаправлення.

@SessionAttribute

Призначений для забезпечення доступу до будь-якого атрибуту сесії, на відміну від атрибутів моделі, що зберігаються в сесії в результаті оголошення анотації @SessionAttributes на рівні класу.

@RequestAttribute

Призначений для забезпечення доступу до атрибутів запиту.

Значення, що повертаються

Методи з анотацією @ExceptionHandler підтримують наступні значення, що повертаються:

Return value Description

@ResponseBody

Значення, що повертається, перетворюється через екземпляри HttpMessageConverter і записується у відповіді.

HttpEntity<B>, ResponseEntity<B>

Значення, що повертається, зазначає, що повна відповідь (включно з HTTP-заголовками та тілом) буде перетворена через екземпляри HttpMessageConverter і записана у відповідь.

String

Ім'я подання, яке має бути розпізнане за допомогою реалізацій ViewResolver та використовуватися разом з неявною моделлю — визначається через об'єкти команд та методи з анотацією @ModelAttribute. Метод обробника також може програмно доопрацювати модель, оголосивши аргумент Model (описаний раніше).

View

Екземпляр View, призначений для візуалізації разом з неявною моделлю — визначається через об'єкти команд та методи з анотацією @ModelAttribute. Метод обробника може також програмно доопрацювати модель, оголосивши аргумент Model (описаний раніше).

java.util .Map, org.springframework.ui.Model

Атрибути для додавання в неявну модель з ім'ям уявлення, неявно визначеним через RequestToViewNameTranslator.

@ModelAttribute

Атрибут, що додається до моделі з іменем представлення, неявно визначеним через RequestToViewNameTranslator.

Зверни увагу, що анотація @ModelAttribute є необов'язковою. Див. розділ "Будь-яке інше значення, що повертається" в кінці цієї таблиці.

ModelAndView object

Атрибути подання та моделі, що використовуються, і, за необхідності, статус відповіді.

void

Вважається, що метод з типом void, що повертається (або значенням, що повертається null) повністю обробив відповідь, якщо він також має аргумент ServletResponse або OutputStream, або анотацію @ResponseStatus. Те саме вірно, якщо контролер виконав позитивну перевірку ETag або тимчасової мітки lastModified.

Якщо жодне з вищеперелічених значень не вірне, повертається тип void може також вказувати на "відсутність тіла відповіді" для REST-контролерів або вибір імені подання за замовчуванням для HTML-контролерів.

Будь-яке інше значення, що повертається

Якщо значення, що повертається, не відповідає жодному з перерахованих і не є простим типом (як визначено BeanUtils#isSimpleProperty), за замовчуванням воно вважається атрибутом моделі, який має бути доданий до моделі. Якщо це простий тип, він залишається невирішеним.

Робота з винятками в REST API

Загальною вимогою для служб REST є включення інформації про помилку до тіла відповіді. Spring Framework не автоматично цього не робить, оскільки подання інформації про помилку в тілі відповіді залежить від конкретної програми. Однак анотація @RestController може використовувати методи, позначені анотацією @ExceptionHandler, зі значенням ResponseEntity, що повертається, для встановлення статусу і тіла відповіді. Такі методи також можуть бути оголошені в класах, анотованих @ControllerAdvice, щоб можна було застосовувати їх глобально.

Додатки, що реалізують глобальну обробку винятків із докладним описом помилки в тілі відповіді, повинні враховувати можливість розширення ResponseEntityExceptionHandler, який забезпечує обробку винятків, що виникають у Spring MVC, і надає перехоплювачі для налаштування тіла відповіді. Щоб скористатися цим, створи підклас ResponseEntityExceptionHandler, анотуй його за допомогою @ControllerAdvice, перевизнач необхідні методи та оголоси його як бін Spring.