← Назад к вопросам

Какие знаешь подходы проектирования REST API?

2.0 Middle🔥 221 комментариев
#REST API и микросервисы#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Подходы проектирования REST API

REST (Representational State Transfer) — архитектурный стиль для построения веб-сервисов, основанный на стандартах HTTP и принципах распределенных систем. Правильное проектирование REST API критично для масштабируемости и удобства интеграции клиентов.

1. Ресурсо-ориентированный подход

Основной принцип REST — все в системе представляется как ресурсы, а не действия. Каждый ресурс имеет уникальный URI:

// Правильно — ресурсы
GET    /api/v1/users              // Получить список пользователей
GET    /api/v1/users/{id}         // Получить конкретного пользователя
POST   /api/v1/users              // Создать нового пользователя
PUT    /api/v1/users/{id}         // Обновить пользователя
DELETE /api/v1/users/{id}         // Удалить пользователя

// Неправильно — глаголы в URI
GET    /api/v1/getUsers
POST   /api/v1/createUser
POST   /api/v1/deleteUser/{id}

Вложенные ресурсы для коллекций:

GET    /api/v1/users/{userId}/posts          // Посты конкретного пользователя
POST   /api/v1/users/{userId}/posts          // Создать пост
GET    /api/v1/users/{userId}/posts/{postId} // Получить конкретный пост

2. HTTP методы и семантика

GET — безопасен (не изменяет состояние), идемпотентен (повторные вызовы возвращают одинаковый результат):

@GetMapping("/users/{id}")
public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
    return userService.findById(id)
        .map(ResponseEntity::ok)
        .orElse(ResponseEntity.notFound().build());
}

POST — создает новый ресурс, не идемпотентен (повторные вызовы создают разные ресурсы):

@PostMapping("/users")
public ResponseEntity<UserDto> createUser(@RequestBody CreateUserRequest request) {
    UserDto created = userService.create(request);
    return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

PUT — полностью заменяет ресурс, идемпотентен:

@PutMapping("/users/{id}")
public ResponseEntity<UserDto> updateUser(@PathVariable Long id, 
                                          @RequestBody UpdateUserRequest request) {
    UserDto updated = userService.update(id, request);
    return ResponseEntity.ok(updated);
}

PATCH — частичное обновление ресурса:

@PatchMapping("/users/{id}")
public ResponseEntity<UserDto> partialUpdate(@PathVariable Long id,
                                             @RequestBody JsonPatch patch) {
    UserDto updated = userService.patch(id, patch);
    return ResponseEntity.ok(updated);
}

DELETE — удаляет ресурс, идемпотентен:

@DeleteMapping("/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
    userService.delete(id);
    return ResponseEntity.noContent().build();
}

3. Коды ответов (HTTP Status Codes)

  • 200 OK — успешный GET, PUT, PATCH
  • 201 Created — успешный POST с созданием ресурса
  • 204 No Content — успешный DELETE или обновление без тела ответа
  • 400 Bad Request — ошибка валидации клиента
  • 401 Unauthorized — требуется аутентификация
  • 403 Forbidden — недостаточно прав доступа
  • 404 Not Found — ресурс не найден
  • 409 Conflict — конфликт состояния (например, дублирование)
  • 500 Internal Server Error — ошибка сервера

4. Версионирование API

URL версионирование — простой способ управления версиями:

@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 { }

@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 { }

Header версионирование — более элегантный подход:

@GetMapping("/users/{id}")
public ResponseEntity<UserDto> getUser(@PathVariable Long id,
                                       @RequestHeader("API-Version") String version) {
    if ("2".equals(version)) {
        return ResponseEntity.ok(userService.findByIdV2(id));
    }
    return ResponseEntity.ok(userService.findById(id));
}

5. Фильтрация, сортировка и пагинация

@GetMapping("/users")
public ResponseEntity<Page<UserDto>> listUsers(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "20") int size,
        @RequestParam(required = false) String sort,
        @RequestParam(required = false) String filter) {
    
    Pageable pageable = PageRequest.of(page, size, Sort.by(sort));
    Page<UserDto> users = userService.findAll(pageable, filter);
    return ResponseEntity.ok(users);
}

6. Content Negotiation

@GetMapping(value = "/users/{id}", produces = {"application/json", "application/xml"})
public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
    return ResponseEntity.ok(userService.findById(id));
}

7. Обработка ошибок

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
        ErrorResponse error = new ErrorResponse(
            "NOT_FOUND",
            ex.getMessage(),
            System.currentTimeMillis()
        );
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }
}

8. Безопасность

  • Используй HTTPS для всех запросов
  • Реализуй аутентификацию (OAuth 2.0, JWT)
  • Валидируй входные данные на сервере
  • Используй CORS для контроля доступа
  • Применяй rate limiting для защиты от DDoS
  • Логируй важные события

Правильное проектирование REST API обеспечивает интуитивность, масштабируемость и безопасность системы.