JavaRush /Курси /Модуль 5. Spring /Глобальна обробка помилок через @ControllerAdvice

Глобальна обробка помилок через @ControllerAdvice

Модуль 5. Spring
Рівень 8 , Лекція 5
Відкрита

Зробімо крок уперед і пориньмо в глобальну обробку помилок за допомогою потужного інструменту Spring — @ControllerAdvice. У цій лекції ти дізнаєшся, як централізувати обробку всіх помилок додатку, зробити її більш гнучкою і акуратною.


Що таке @ControllerAdvice?

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

Уяви, що в тебе є додаток з великою кількістю контролерів, де різні ендпоінти можуть кидати однакові виключення (наприклад, MethodArgumentNotValidException при помилках валідації). Якщо писати обробники в кожному контролері, код почне дублюватись і ним буде важко керувати. @ControllerAdvice вирішує цю проблему: один раз оголосивши глобальний обробник, ти можеш уніфікувати логіку обробки помилок.

Як це працює?

Spring автоматично реєструє класи з анотацією @ControllerAdvice і застосовує їх до всіх контролерів в твоєму додатку. Ти можеш комбінувати це з анотацією @ExceptionHandler, щоб визначити, які виключення обробляти.


Як налаштувати глобальний обробник з використанням @ControllerAdvice?

Крок 1. Створення класу з анотацією @ControllerAdvice

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


package com.example.demo.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

    // Обробка загального виключення
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGenericException(Exception ex) {
        return new ResponseEntity<>("Сталася помилка: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Що тут відбувається?

  • Ми використовуємо @ControllerAdvice, щоб повідомити Spring, що цей клас буде обробляти виключення для всіх контролерів.
  • У методі handleGenericException ми ловимо будь-яке виключення (Exception.class) і повертаємо просте повідомлення про помилку з HTTP-статусом 500 Internal Server Error.

Крок 2. Обробка конкретних виключень

Часто нам потрібно обробляти не всі виключення підряд, а конкретні типи, наприклад помилки валідації або ситуації, коли ресурс не знайдено. Додамо обробники для таких випадків.


import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;

@ControllerAdvice
public class GlobalExceptionHandler {

    // Обробка помилок валідації
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
        return new ResponseEntity<>("Помилка валідації: перевірте передані дані", HttpStatus.BAD_REQUEST);
    }

    // Обробка ресурсу, який не знайдено
    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResponseEntity<String> handleResourceNotFoundException(ResourceNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }
}

Зверни увагу:

  • Для обробки помилок валідації ми додали обробник, який ловить MethodArgumentNotValidException. Це виключення генерується автоматично при використанні @Valid.
  • Для помилок ресурсу ми припускаємо, що в нас є власне виключення ResourceNotFoundException. Це приклад кастомного виключення, яке може бути викинуте з сервісного шару.

Крок 3. Створення кастомного виключення

Якщо в тебе є ситуації, які вимагають власної логіки обробки, краще створити своє виключення. Ось приклад:


package com.example.demo.exception;

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

Тепер це виключення можна викидати, наприклад, у сервісі:


if (resource == null) {
    throw new ResourceNotFoundException("Ресурс з ID " + id + " не знайдено");
}

Приклад: Спільна обробка помилок

Зберемо все разом. Нехай у нас є простий контролер, де є метод для отримання ресурсу по ID. Якщо ресурс не знайдено, ми викидаємо ResourceNotFoundException. Якщо дані некоректні, Spring автоматично викине MethodArgumentNotValidException.

Контролер


package com.example.demo.controller;

import com.example.demo.exception.ResourceNotFoundException;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;

@RestController
@RequestMapping("/api/resources")
public class ResourceController {

    @GetMapping("/{id}")
    public String getResource(@PathVariable @Min(1) Long id) {
        // Імітація пошуку ресурсу
        if (id == 999) {
            throw new ResourceNotFoundException("Ресурс з ID " + id + " не знайдено");
        }
        return "Ресурс знайдено: ID = " + id;
    }
}

Особливості:

  • Ми застосовуємо анотацію @Min(1) для перевірки ID. Якщо ID менше 1, Spring автоматично викине MethodArgumentNotValidException.
  • Якщо ресурс з ID 999 не знайдено, викидається наше кастомне виключення.

Результати

  1. Якщо ID менше 1:
    
    HTTP 400 BAD REQUEST
    Помилка валідації: перевірте передані дані
    
  2. Якщо ресурс не знайдено (ID = 999):
    
    HTTP 404 NOT FOUND
    Ресурс з ID 999 не знайдено
    
  3. Якщо відбувається будь-яке інше виключення:

    
    HTTP 500 INTERNAL SERVER ERROR
    Сталася помилка: [текст виключення]
    

Переваги використання @ControllerAdvice

  1. Централізація обробки помилок: увесь код обробки помилок знаходиться в одному місці, що спрощує підтримку додатку.
  2. Однорідність: усі помилки обробляються однаково, що робить API більш передбачуваним і зручним для клієнтів.
  3. Гнучкість: ти можеш додавати обробники для різних типів виключень і налаштовувати їх під свої потреби.

Покращення користувацького досвіду

Глобальна обробка помилок — це не тільки про комфорт програміста, але й про зручність для користувачів твого API або веб-додатку. Ти можеш зробити помилки більш інформативними, локалізувати їх для підтримки кількох мов і логувати їх для аналізу та усунення проблем.

У наступних лекціях ми торкнемося цих аспектів: як формувати користувацькі повідомлення про помилки, як їх локалізувати і як впровадити ефективне логування.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ