Ответ
Имею опыт централизованной обработки исключений в Spring MVC с использованием @ControllerAdvice и @ExceptionHandler. Это позволяет отделить бизнес-логику от кода обработки ошибок и возвращать клиенту структурированные, консистентные ответы.
Почему @ControllerAdvice — best practice: Глобальная обработка исключений для всех контроллеров, избегание дублирования кода (try-catch в каждом методе), унификация формата ошибок.
Пример глобального обработчика исключений:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"NOT_FOUND",
ex.getMessage(),
Instant.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationErrors(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
ErrorResponse error = new ErrorResponse(
"VALIDATION_FAILED",
"Invalid request parameters",
Instant.now(),
errors // Детали ошибок валидации
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAllUncaughtException(Exception ex) {
// Логируем полный stacktrace для внутреннего анализа
logger.error("Unexpected error", ex);
// Клиенту возвращаем общее сообщение без деталей
ErrorResponse error = new ErrorResponse(
"INTERNAL_SERVER_ERROR",
"An internal server error occurred",
Instant.now()
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
// DTO для ответа с ошибкой
record ErrorResponse(String code, String message, Instant timestamp, List<String> details) {
public ErrorResponse(String code, String message, Instant timestamp) {
this(code, message, timestamp, null);
}
}
Ключевые практики:
- Специфичные исключения: Создание собственных иерархий исключений (например,
BusinessException,ValidationException). - HTTP-статусы: Корректное сопоставление типа исключения с HTTP-статусом (404 для "Не найдено", 400 для невалидных данных, 409 для конфликтов).
- Логирование: Все необработанные исключения (
Exception.class) должны логироваться на уровне ERROR для последующего анализа. - Безопасность: В ответах клиенту не должно быть stacktrace или внутренних деталей реализации, которые могут помочь атакующему.