Ответ
В Spring MVC для глобальной обработки исключений и выполнения перенаправления (redirect) используются аннотации @ControllerAdvice и @ExceptionHandler.
1. Создание пользовательского исключения
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
// Можно добавить дополнительные поля, например, userId
}
2. Создание класса-обработчика исключений (@ControllerAdvice)
Класс, помеченный @ControllerAdvice, перехватывает исключения, выброшенные в любом контроллере.
@ControllerAdvice // Объявляет глобальный обработчик исключений
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class) // Указывает тип обрабатываемого исключения
public String handleUserNotFoundException(UserNotFoundException ex,
HttpServletRequest request,
RedirectAttributes redirectAttributes) {
// 1. Логирование исключения (обязательно для отладки)
logger.error("User not found for request: " + request.getRequestURI(), ex);
// 2. Добавление flash-атрибута для передачи сообщения на страницу регистрации
// Flash-атрибуты живут ровно один redirect, что идеально подходит для этой задачи.
redirectAttributes.addFlashAttribute("errorMessage",
"Пользователь не найден. Пожалуйста, зарегистрируйтесь.");
// 3. Выполнение redirect на URL страницы регистрации
return "redirect:/register"; // Предполагается, что есть маппинг на "/register"
}
}
3. Контроллер, который бросает исключение
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String getUserProfile(@PathVariable Long id, Model model) {
User user = userService.findById(id)
.orElseThrow(() -> new UserNotFoundException("User with id " + id + " not found"));
model.addAttribute("user", user);
return "profile";
}
}
4. Контроллер и страница регистрации
@Controller
public class RegistrationController {
@GetMapping("/register")
public String showRegistrationForm(Model model) {
// Атрибут "errorMessage" будет автоматически подставлен из flash-атрибута
// если он был установлен в обработчике исключений.
if (!model.containsAttribute("userDto")) {
model.addAttribute("userDto", new UserDto());
}
return "registration-page"; // Имя Thymeleaf/HTML шаблона
}
}
В шаблоне registration-page.html (например, Thymeleaf) можно отобразить сообщение:
<div th:if="${errorMessage}" class="alert alert-danger">
<span th:text="${errorMessage}"></span>
</div>
<!-- Остальная форма регистрации -->
Ключевые моменты:
@ControllerAdvice+@ExceptionHandler— стандартный и мощный механизм для централизованной обработки исключений.RedirectAttributesиспользуется для безопасной передачи данных между запросами при redirect.- Flash-атрибуты (
addFlashAttribute) идеальны для передачи сообщений об ошибках, так как они не попадают в URL и существуют один запрос. - Всегда логируйте исключения для последующего анализа.
- Этот подход отделяет логику обработки ошибок от бизнес-логики контроллеров, делая код чище и поддерживаемее.
Ответ 18+ 🔞
А, ну ты глянь, какой у нас тут академический разговор про исключения в Spring MVC. Прям как в институте, блядь. Слушай, а давай я тебе на пальцах объясню, как эту хуйню на практике делать, а то смотрю — ты уже глаза закатываешь от этих @ControllerAdvice.
Представь себе, у тебя есть контроллер, который юзеров ищет. Ну, классика:
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String getUserProfile(@PathVariable Long id, Model model) {
// Вот тут мы наивно думаем, что юзер всегда найдётся
User user = userService.findById(id)
.orElseThrow(() -> new UserNotFoundException("User with id " + id + " not found"));
model.addAttribute("user", user);
return "profile";
}
}
И вот этот самый UserNotFoundException — это наша самописная граната. Выглядит проще пареной репы:
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
А теперь, внимание, сценарий: заходит какой-нибудь умник по ссылке /user/999999, а юзера с таким айдишником — нихуя. Контроллер херачит исключение, и если ничего не сделать, пользователю в ебучем браузере вывалится стопка трейсов на пол-экрана. Красота, да? Пиздец как профессионально.
Так вот, чтобы не выглядеть полными лузерами, мы делаем глобального "уборщика" за всеми контроллерами. В Spring это зовётся @ControllerAdvice. Это такая сущность, которая ловит все выброшенные исключения, как сетью, и говорит: "Не волнуйся, пацан, я всё уладю".
Создаём класс-спасатель:
@ControllerAdvice // Эта хуйня говорит: "Я тут главный по тарелочкам"
public class GlobalExceptionHandler {
// Логгер — наш лучший друг, чтобы потом понять, что пошло по пизде
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(UserNotFoundException.class) // Ловим конкретно наше исключение
public String handleUserNotFoundException(UserNotFoundException ex,
HttpServletRequest request,
RedirectAttributes redirectAttributes) {
// Шаг 1: Зафиксировать преступление. Логируем, блядь, всегда логируем!
logger.error("Запрос: {} — юзер не найден. Ошибка: {}", request.getRequestURI(), ex.getMessage());
// Шаг 2: Готовим "записку" для следующей страницы.
// Используем flash-атрибут — это как передать бумажку через одного человека.
// Она живёт ровно один редирект, а потом исчезает. Магия, ёпта!
redirectAttributes.addFlashAttribute("errorMessage",
"Э, дружок-пирожок, такого пользователя нет. Давай-ка зарегистрируйся сначала!");
// Шаг 3: Делаем редирект куда надо, а не показываем белую страницу с ошибкой.
return "redirect:/register";
}
}
Теперь, когда наш UserNotFoundException вылетает, этот обработчик его ловит, тихо матерится в лог, и незаметно для пользователя делает редирект на страницу регистрации (/register). Причём вместе с ним летит наше сообщение errorMessage.
На стороне регистрации всё просто. Контроллер даже не парится:
@Controller
public class RegistrationController {
@GetMapping("/register")
public String showRegistrationForm(Model model) {
// Flash-атрибут 'errorMessage' автоматом подтянется в модель, если он был передан.
// Если его нет — ну и хуй с ним, работаем дальше.
if (!model.containsAttribute("userDto")) {
model.addAttribute("userDto", new UserDto());
}
return "registration-page"; // Показываем шаблон
}
}
А в шаблоне (registration-page.html, если используем Thymeleaf) просто проверяем:
<!-- Если сообщение об ошибке прилетело — показываем его -->
<div th:if="${errorMessage}" class="alert alert-danger" style="color: red; font-weight: bold;">
<span th:text="${errorMessage}"></span>
</div>
<!-- А тут сама форма регистрации -->
И вуаля! Пользователь вместо стопки трейсов видит вежливое (или не очень) предложение зарегистрироваться. Всё чисто, аккуратно, и никто не обосрался.
Итоги, чтобы не ебать мозг:
@ControllerAdvice— это глобальный папочка, который за всеми присматривает.@ExceptionHandler— ловушка для конкретного типа исключений.RedirectAttributesиaddFlashAttribute— твой лучший способ передать данные при редиректе, не палясь в URL.- ЛОГГЕР — святое дело. Без него ты как слепой котёнок в подвале.
- Вся эта конструкция позволяет не превращать каждый контроллер в помойку с try-catch блоками. Красота, блядь!