Как реализовать обработку ошибок в Spring для возврата фронту унифицированного ответа с сообщением?
Что проверяют
Знаете ли вы централизованную обработку исключений и договариваетесь ли с фронтом о формате ошибки заранее, а не в каждом контроллере по-своему. Следом спрашивают, как вернуть ошибки валидации по полям и что будет с исключением из фильтра Spring Security.
Как отвечать
Начните с формата. В Spring Framework 6 и Spring Boot 3 есть готовый ProblemDetail по стандарту Problem Details (RFC 9457, преемник RFC 7807): поля type, title, status, detail, instance и свои свойства через setProperty(). Своя модель ошибки тоже допустима, главное, чтобы она была одна на весь API и содержала машинно-читаемый код, по которому фронт выбирает текст, а не разбирает message.
Затем обработчик:
@RestControllerAdvice: класс с методами@ExceptionHandler, одна точка для всех контроллеров;- наследование от
ResponseEntityExceptionHandler: он уже обрабатывает стандартные исключения MVC (MethodArgumentNotValidException,HttpMessageNotReadableException, неподдерживаемый метод) и отдаётProblemDetail. Переопределяете нужный метод и добавляете список ошибок по полям изgetBindingResult().getFieldErrors(); - доменные исключения:
NotFoundException,ConflictExceptionмаппятся на 404 и 409. Можно унаследоваться отErrorResponseException, тогда статус и тело задаются прямо в исключении; - ловушка для
Exception: всё непредвиденное превращается в 500 с нейтральным текстом, а подробности и стектрейс уходят в лог вместе с trace id, который возвращается и клиенту для поиска по логам.
Если своего advice нет, свойство spring.mvc.problemdetails.enabled=true включает ProblemDetail для стандартных исключений MVC.
Где спотыкаются
Ждут, что @RestControllerAdvice поймает ошибку из фильтра. Исключения Spring Security возникают до DispatcherServlet, для них нужны AuthenticationEntryPoint и AccessDeniedHandler, отдающие тот же формат.
Отдают клиенту ex.getMessage() любого исключения. Так наружу утекают куски SQL, имена таблиц и внутренние пути.
Возвращают 200 с полем success: false. Фронт, мониторинг и повторы опираются на HTTP-статус, и такой ответ ломает их все сразу.