Что такое RESTful API дизайн
В основе RESTful дизайна лежит ресурсно-ориентированная архитектура (Resource-Oriented Architecture, ROA):
🟢 Junior Level
RESTful API дизайн — это подход к проектированию программных интерфейсов, базирующийся на архитектурных принципах REST (Representational State Transfer) и семантике протокола HTTP.
В основе RESTful дизайна лежит ресурсно-ориентированная архитектура (Resource-Oriented Architecture, ROA):
- Ресурсы (Resources) — это сущности предметной области (пользователи, заказы, товары). Ресурсы всегда идентифицируются через URI с использованием существительных во множественном числе (
/users,/orders). - Действия (Actions) — выражаются стандартными HTTP-методами (
GET,POST,PUT,PATCH,DELETE). - Результат операции — выражается стандартными HTTP статус-кодами (
200,201,204,400,404,500).
Канонический пример RESTful эндпоинтов
| HTTP Метод | URI | Семантическое назначение | Успешный HTTP статус |
|---|---|---|---|
GET |
/api/v1/users |
Получить список пользователей (с пагинацией) | 200 OK |
GET |
/api/v1/users/42 |
Получить конкретного пользователя с ID 42 | 200 OK |
POST |
/api/v1/users |
Создать нового пользователя | 201 Created (+ Location) |
PUT |
/api/v1/users/42 |
Полностью заменить данные пользователя 42 | 200 OK или 204 No Content |
PATCH |
/api/v1/users/42 |
Частично обновить поля пользователя 42 | 200 OK |
DELETE |
/api/v1/users/42 |
Удалить пользователя 42 | 204 No Content |
Главные антипаттерны в именовании
- ❌
GET /api/getUsers— глагол в URI дублирует HTTP-методGET. - ❌
POST /api/deleteUser?id=42— методPOSTиспользуется вместо идемпотентногоDELETE. - ❌
POST /api/user/save— RPC-стиль в стиле старых SOAP-сервисов.
🟡 Middle Level
Модель зрелости Ричардсона (Richardson Maturity Model)
Леонард Ричардсон выделил 4 уровня соответствия веб-сервиса истинному REST:
┌────────────────────────────────────────────────────────┐
│ Level 3: HATEOAS (Hypermedia as the Engine of State) │
├────────────────────────────────────────────────────────┤
│ Level 2: HTTP Verbs & Status Codes (Стандарт отрасли) │
├────────────────────────────────────────────────────────┤
│ Level 1: Resources & Individual URIs (/users/42) │
├────────────────────────────────────────────────────────┤
│ Level 0: The Swamp of POX (RPC over HTTP POST) │
└────────────────────────────────────────────────────────┘
- Level 0 (The Swamp of POX): HTTP используется как слепой транспортный туннель. Все запросы шлются на один URL (
/api/service) методомPOST(SOAP, XML-RPC, JSON-RPC). - Level 1 (Resources): Появление раздельных URI для каждой сущности (
/users/1,/orders/2), но взаимодействие идет через один или два метода (например, все действия —POST). - Level 2 (HTTP Verbs & Status Codes): Золотой стандарт коммерческой разработки. Используются правильные HTTP методы (
GET,POST,PUT,DELETE), статусы ответов (201,204,404) и заголовки. - Level 3 (HATEOAS): Сервер вместе с данными возвращает ссылки на возможные дальнейшие действия клиента. В 95% промышленных систем считается избыточным из-за высокого оверхеда на парсинг и передачу ссылок.
Фильтрация, пагинация и сортировка
Для выборки подмножеств ресурсов используются Query Parameters, а не создание новых URI:
- Пагинация (Offset / Limit):
GET /api/v1/orders?offset=20&limit=10 - Курсорная пагинация (Cursor-based):
GET /api/v1/orders?cursor=eyJpZCI6MTAxfQ&limit=20(критично для лент новостей и больших таблиц, исключает сдвиг записей). - Сортировка:
GET /api/v1/products?sort=price,desc&sort=name,asc - Фильтрация:
GET /api/v1/tickets?status=OPEN&priority=HIGH
Решение проблемы N+1 в REST (Sparse Fieldsets и Embedding)
В отличие от GraphQL, REST API часто страдает от избыточной выборки (Over-fetching) или проблемы N+1 HTTP-запросов. В RESTful дизайне это решается двумя паттернами:
- Sparse Fieldsets: выборка только нужных полей клиента:
GET /api/v1/users/42?fields=id,email,displayName - Resource Embedding: внедрение связанных сущностей:
GET /api/v1/orders/101?embed=customer,items
🔴 Senior Level
Design-First (API-First) vs Code-First
В энтерпрайз-разработке выбор подхода определяет скорость и качество интеграции:
| Критерий | API-First (Design-First) | Code-First |
|---|---|---|
| Методология | Сначала пишется OpenAPI 3.1 спецификация (YAML), затем генерируется код. | Сначала пишутся Spring контроллеры, документация генерируется SpringDoc. |
| Параллельная работа | Фронтенд, мобильные клиенты и бэкенд разрабатываются одновременно по Mock-серверу (Prism). | Клиентские команды вынуждены ждать написания бэкенда. |
| Соответствие контракту | 100% гарантия совпадения документации и API. Линтинг через Spectral. | Документация может расходиться с реальным поведением при сложных маппингах. |
| Инструменты | openapi-generator-maven-plugin, Stoplight, SwaggerHub. |
springdoc-openapi-starter-webmvc-ui. |
Архитектура Spring MVC: обработка REST запросов
За кулисами Spring Boot за обработку запроса отвечают:
DispatcherServletполучает входящий HTTP запрос.RequestMappingHandlerMappingпо таблице маршрутов находит подходящий метод класса@RestController.HandlerAdapterподготавливает вызов метода, активируяHandlerMethodArgumentResolver(парсинг@RequestBody,@PathVariable,@RequestParam).HttpMessageConverter(JacksonMappingJackson2HttpMessageConverter) десериализует тело запроса из JSON и сериализует ответ в JSON.
Образцовый Spring Boot 3 RESTful контроллер
package com.example.controller;
import com.example.dto.CreateUserRequest;
import com.example.dto.UpdateUserRequest;
import com.example.dto.UserResponseDto;
import com.example.service.UserService;
import jakarta.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// 1. Получение коллекции с пагинацией и сортировкой: 200 OK
@GetMapping
public ResponseEntity<Page<UserResponseDto>> getUsers(
@RequestParam(required = false) String status,
@PageableDefault(size = 20, sort = "createdAt") Pageable pageable) {
return ResponseEntity.ok(userService.findAll(status, pageable));
}
// 2. Получение одного ресурса: 200 OK
@GetMapping("/{id}")
public ResponseEntity<UserResponseDto> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(userService.findById(id));
}
// 3. Создание ресурса: 201 Created + заголовок Location
@PostMapping
public ResponseEntity<UserResponseDto> createUser(@Valid @RequestBody CreateUserRequest request) {
UserResponseDto created = userService.create(request);
// Формируем URI созданного ресурса: /api/v1/users/{id}
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(created.id())
.toUri();
return ResponseEntity.created(location).body(created);
}
// 4. Полная замена: 200 OK
@PutMapping("/{id}")
public ResponseEntity<UserResponseDto> replaceUser(@PathVariable Long id,
@Valid @RequestBody UpdateUserRequest request) {
return ResponseEntity.ok(userService.update(id, request));
}
// 5. Удаление ресурса: 204 No Content
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}
🎯 Шпаргалка для интервью
30-секундный ответ
RESTful API дизайн — это архитектурный подход к проектированию веб-сервисов, при котором API структурируется вокруг ресурсов (существительные во множественном числе), действия над ними выполняются стандартными HTTP-методами (
GET,POST,PUT,PATCH,DELETE), а результат возвращается стандартными HTTP-статус-кодами.Согласно модели Ричардсона (Richardson Maturity Model), современным промышленным стандартом является Level 2 (Resources + HTTP Verbs + Status Codes). Фильтрация и пагинация выносятся в query-параметры. В enterprise-проектах стандартом является подход API-First (Design-First) с описанием контракта в OpenAPI 3.1 до написания кода.
4 каверзных вопроса с ответами
-
Что такое Richardson Maturity Model и почему Level 3 (HATEOAS) редко используется на практике? Ответ: Модель зрелости описывает путь от чистого RPC (Level 0) через ресурсы (Level 1) и HTTP-методы со статусами (Level 2) к гипермедиа HATEOAS (Level 3). HATEOAS редко применяется из-за высокого оверхеда на формирование графа ссылок, раздувания размера JSON на 30–50% и сложности его поддержки на мобильных клиентах, которым проще работать с фиксированными контрактами OpenAPI.
-
Зачем при создании ресурса методом POST возвращать заголовок Location? Ответ: Заголовок
Location: /api/v1/users/42указывает клиенту точный канонический URI вновь созданного подчиненного ресурса в соответствии со спецификацией RFC 9110, позволяя клиенту сразу перейти к чтению объекта без ручной конкатенации строк. -
В чем разница между подходом Design-First и Code-First при проектировании REST API? Ответ: При Design-First сначала создается OpenAPI-контракт, согласовываются схемы данных между бэкендом, фронтендом и мобильными разработчиками, и только затем генерируется код. При Code-First разработчики сначала пишут контроллеры, а документация генерируется постфактум. Design-First минимизирует интеграционные риски и позволяет вести разработку параллельно.
-
Как в RESTful дизайне правильно отобразить нестандартное действие, например, «заблокировать пользователя» или «перевести средства»? Ответ: Через подчиненные ресурсы состояний:
POST /users/42/block-actionsили изменение состояния через PATCH:PATCH /users/42 {"status": "BLOCKED"}. Для финансовых операций создается ресурс транзакции:POST /transfersс телом запроса, а не вызов глагола/users/42/transferMoney.
Красные флаги (чего категорически нельзя говорить)
- ❌ «В REST API глаголы в URL — это признак хорошего тона для понятности» (глаголы в URI нарушают принципы REST; действие обязано выражаться HTTP-методом).
- ❌ «Для всех операций нужно возвращать 200 OK, а статус ошибки передавать в поле JSON status» (ломает стандарт HTTP, лишает промежуточные кэши и мониторинг возможности отслеживать сбои).
- ❌ «REST API всегда требует использования HATEOAS, иначе это не REST» (в прагматичной индустрии подавляющее большинство сервисов работают на Richardson Level 2).
- ❌ «Query параметры и Path variables абсолютно взаимозаменяемы» (Path переменные идентифицируют ресурс, а Query параметры фильтруют, сортируют и настраивают пагинацию выборки).