Обработка исключений в Spring Boot REST сервисе
Быстрый ответ
Для эффективного управления исключениями в REST-сервисе Spring Boot целесообразно использовать композицию аннотаций @RestControllerAdvice
и @ExceptionHandler
. Это позволяет контролировать ошибки на разнообразных уровнях. В качестве примера может служить следующий код:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception e) {
String errorUUID = logErrorToNoSql(e);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("К сожалению, произошла ошибка. Обратитесь с этим идентификатором: " + errorUUID);
}
private String logErrorToNoSql(Exception e) {
// Здесь мы логируем информацию об ошибке с использованием NoSQL и UUID
return UUID.randomUUID().toString();
}
}
Такой метод обработает любое исключение, возвращая HTTP-статус 500 и идентификатор для отслеживания проблемы. Заметим, что этот метод можно адаптировать под более конкретные типы исключений.
Усовершенствуем ответы API на ошибки с помощью ResponseEntityExceptionHandler
Наследование от класса ResponseEntityExceptionHandler
оптимизирует структуру ответов на исключения в API и обеспечивает единообразие в их обработке:
@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, "Ошибка валидации данных", ex.getBindingResult().toString());
return new ResponseEntity<>(apiError, headers, status);
}
// ApiError — ваш собственный класс по передаче информации об ошибке
}
Не просто 404 – Кастомные ответы на 404
Для настройки обработки NoHandlerFoundException
в Spring Boot установите параметр spring.mvc.throw-exception-if-no-handler-found=true
в файле application.properties
. В вашем классе-советнике можно добавить следующий обработчик:
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, WebRequest request) {
ApiError apiError = new ApiError(HttpStatus.NOT_FOUND, "Ой, похоже, такой страницы здесь нет.", ex.getMessage());
return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
}
Когда жизнь бросает вызов, перехватываем их с помощью аспектов
Для конвертации ошибок в исключения обратите внимание на аспектно-ориентированный подход:
@Aspect
@Component
public class ErrorHandlingAspect {
@Around("execution(* com.yourcompany.*.*(..))")
public Object handleErrors(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return joinPoint.proceed();
} catch (Error error) {
throw new CustomException("Извините, произошла ошибка. Теперь это исключение под нашим контролем.", error);
}
}
}
Использование аспектов требует осторожности, чтобы не внести их поддержку там, где это не целесообразно.
Ловушки, которые следует избегать на пути обработки исключений
Избегайте использования аннотации @EnableWebMvc
, которая прекращает работу автоконфигурации Spring Boot. Вместо этого опирайтесь на @ComponentScan
и @EnableAutoConfiguration
, чтобы воспользоваться преимуществами Spring Boot. Отключение автоматического маппинга статических ресурсов может ускорить работу чистого REST-сервиса.
Тестирование, раз, два, три... с MockMvc!
Для проверки обработки исключений используйте модульные тесты с MockMvc:
@RunWith(SpringRunner.class)
@WebMvcTest
public class ExceptionHandlerControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void whenMethodArgumentNotValid_thenBadRequest() throws Exception {
mockMvc.perform(post("/endpoint")
.content("{...}")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error").value("Ошибка валидации данных"));
}
}
Правильно написанные тесты помогут удостовериться, что обработка исключений работает корректно.
Визуализация
REST-сервис можно представить как завод:
- Конечные точки 💻: Узлы процессов, выпускающие продукцию.
- Исключения ⚠️: Непредвиденные препятствия в рабочем процессе.
- Обработчик 🛠️: Команда технической поддержки для всех рабочих узлов.
Обработчики исключений в Spring Boot выступают в роли контроллеров качества:
- Входящие исключения ⚡: Ошибки производства, требующие немедленного вмешательства.
- ExceptionHandler 👮♀️: Обеспечение упорядоченной обработки возникающих ситуаций.
В конечном итоге, каждая проблема решается, а клиенту возвращается правильно сформированный HTTP-ответ.
Завершение
Используйте структурирование классов ошибок для создания унифицированного вида ошибок.
Научитесь определять, что лучше применять в вашем случае – автоматическое конфигурирование или нет, и не включайте лишнюю функциональность.
Применяйте аннотацию @ResponseStatus
непосредственно к исключениям для явной установки HTTP-статуса, когда это возможно.
Рассмотрите возможность использования ModelAndView
для отображения пользовательской страницы ошибки.
Продолжайте изучать открытый код и постоянно обогащайте свой опыта новыми подходами и решениями.
Избегайте излишних спецификаций в обработке исключений, стремитесь к более общему решению.
Полезные материалы
- Документация по Spring Boot — для тех, кто хочет глубже погрузиться в тему обработки ошибок в Spring Boot.
- Начало работы | Создание REST-сервисов с помощью Spring — пошаговый путь к созданию REST-сервисов в Spring.
- Глобальная обработка исключений с @ControllerAdvice – DZone — освоение глобальной обработки исключений в Spring Boot.
- Модульное тестирование контроллеров Spring MVC: REST API – Petri Kainulainen — модульные тесты – обязательный этап в разработке высококачественных REST-контроллеров.
- Пример использования MockMvc в Spring Boot с @WebMvcTest — MockMvc упрощает тестирование вашего REST API.