Карта / Spring MVC / REST / ExceptionHandler

ExceptionHandler

@ExceptionHandler перехватывает исключения и превращает их в понятный HTTP-ответ, вместо того чтобы клиент получал страшный стектрейс или просто 500 Internal Server Error без объяснений.

@RestControllerAdvice              // применяется ко всем контроллерам сразу
class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    ResponseEntity<String> handleNotFound(UserNotFoundException e) {
        return ResponseEntity.status(404).body(e.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)   // ошибка валидации @Valid
    ResponseEntity<String> handleValidation(MethodArgumentNotValidException e) {
        return ResponseEntity.status(400).body("Некорректные данные: " + e.getMessage());
    }
}

Когда любой контроллер бросает UserNotFoundException — выполнение переходит в этот обработчик, и клиент получает чистый 404 с понятным сообщением, а не голый 500.

Копнуть глубже

@RestControllerAdvice против @ExceptionHandler прямо в контроллере. Можно поставить @ExceptionHandler в самом контроллере — он сработает только для исключений из методов этого контроллера. @RestControllerAdvice — глобальный, перехватывает исключения из любого контроллера приложения, что обычно и нужно: единая, согласованная обработка ошибок по всему API.

Хороший формат ошибки — не просто текст, а структурированный объект, который клиент (фронтенд) может разобрать программно:

record ErrorResponse(String code, String message, Instant timestamp) {}

@ExceptionHandler(UserNotFoundException.class)
ResponseEntity<ErrorResponse> handleNotFound(UserNotFoundException e) {
    return ResponseEntity.status(404)
        .body(new ErrorResponse("USER_NOT_FOUND", e.getMessage(), Instant.now()));
}

Без глобального обработчика любое незапланированное исключение приводит к 500 с полным стектрейсом в ответе — это и неинформативно для клиента, и потенциально раскрывает внутренние детали реализации (риск безопасности).

🎤 Закрыл тему, если можешь объяснить:
• зачем нужен `@ExceptionHandler` и как он превращает исключение в HTTP-ответ;
• разницу между обработчиком в контроллере и `@RestControllerAdvice` (если дошёл до 2-го слоя).