middle
Как обрабатывать ошибки в REST API?
Правильная обработка ошибок строится на двух принципах: использование корректных HTTP-кодов (4xx/5xx) и возврат структурированного тела ответа с описанием ошибки.
Стандартный формат ответа об ошибке (RFC 7807 — Problem Details):
Пример
{
"type": "https://api.example.com/errors/validation-error",
"title": "Ошибка валидации",
"status": 400,
"detail": "Поле 'email' содержит некорректное значение",
"instance": "/api/users",
"timestamp": "2025-01-15T10:30:00Z",
"errors": [
{
"field": "email",
"message": "Некорректный формат email",
"rejectedValue": "not-an-email"
}
]
}
Реализация в Spring с @ControllerAdvice
// DTO для ответа об ошибке
public record ErrorResponse(
String type,
String title,
int status,
String detail,
String instance,
LocalDateTime timestamp,
List<FieldError> errors
) {
public record FieldError(String field, String message, Object rejectedValue) {}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(
EntityNotFoundException ex, HttpServletRequest request) {
ErrorResponse error = new ErrorResponse(
"https://api.example.com/errors/not-found",
"Ресурс не найден",
404,
ex.getMessage(),
request.getRequestURI(),
LocalDateTime.now(),
null
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(
MethodArgumentNotValidException ex, HttpServletRequest request) {
List<ErrorResponse.FieldError> fieldErrors = ex.getBindingResult()
.getFieldErrors().stream()
.map(fe -> new ErrorResponse.FieldError(
fe.getField(), fe.getDefaultMessage(), fe.getRejectedValue()))
.toList();
ErrorResponse error = new ErrorResponse(
"https://api.example.com/errors/validation-error",
"Ошибка валидации",
400,
"Переданные данные не прошли валидацию",
request.getRequestURI(),
LocalDateTime.now(),
fieldErrors
);
return ResponseEntity.badRequest().body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(
Exception ex, HttpServletRequest request) {
ErrorResponse error = new ErrorResponse(
"https://api.example.com/errors/internal-error",
"Внутренняя ошибка сервера",
500,
"Произошла непредвиденная ошибка",
request.getRequestURI(),
LocalDateTime.now(),
null
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
Spring Boot 3 имеет встроенную поддержку RFC 7807 через ProblemDetail:
Пример
@ExceptionHandler(EntityNotFoundException.class)
public ProblemDetail handleNotFound(EntityNotFoundException ex) {
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
HttpStatus.NOT_FOUND, ex.getMessage());
problem.setTitle("Ресурс не найден");
problem.setType(URI.create("https://api.example.com/errors/not-found"));
return problem;
}
На собеседовании: ключевое — упомянуть RFC 7807 (Problem Details) и
@ControllerAdviceкак стандартные подходы. Частая ошибка — возвращать 200 OK с телом{"success": false}вместо правильных HTTP-кодов ошибок.