HTTP методы
HTTP методы — глаголы REST для действий над ресурсами. GET (чтение, безопасный, идемпотентный, кешируется). POST (создание или действие, не идемпотентный, ID генерирует сервер). PUT (полная замена ресурса, идемпотентный, клиент знает ID, отсутствующие поля удаляются). PATCH (частичное изменение, не обязан быть идемпотентным). DELETE (удаление, идемпотентный). HEAD (только заголовки), OPTIONS (список методов, для CORS). Типичные ошибки: GET для изменения данных, POST для чтения, PUT для частичного обновления, неправильный статус для POST (нужен 201 Created).
Введение: Глаголы для работы с ресурсами
В предыдущей теме мы говорили о ресурсах — существительных в мире API. Пользователь, заказ, товар. Но чтобы что-то сделать с ресурсом, нужны глаголы.
В REST API глаголы — это HTTP методы. Они говорят серверу, какое действие нужно выполнить с ресурсом: прочитать, создать, изменить, удалить. Всего несколько методов покрывают практически все операции, которые могут понадобиться в API.
HTTP методы — это стандартизированные глаголы протокола HTTP, которые указывают на желаемое действие с ресурсом. Каждый метод имеет своё значение, свои правила и свои свойства (безопасность, идемпотентность).
Правильное использование HTTP методов — это не просто “техническая деталь”. Это способ сделать API понятным, предсказуемым и соответствующим веб-стандартам. Когда разработчик видит DELETE /users/123, он сразу понимает, что произойдёт, даже без документации.
Основные HTTP методы
| Метод | Назначение | Безопасный | Идемпотентный | Есть тело запроса | Есть тело ответа |
|---|---|---|---|---|---|
GET | Получить ресурс | Да | Да | Нет | Да |
POST | Создать ресурс или выполнить действие | Нет | Нет | Да | Да |
PUT | Полностью заменить ресурс | Нет | Да | Да | Да (опционально) |
PATCH | Частично изменить ресурс | Нет | Нет* | Да | Да (опционально) |
DELETE | Удалить ресурс | Нет | Да | Опционально | Да (опционально) |
HEAD | Получить только заголовки | Да | Да | Нет | Нет (только заголовки) |
OPTIONS | Получить список доступных методов | Да | Да | Нет | Да |
* PATCH не обязан быть идемпотентным, но может быть.
GET: Получение данных
GET — самый простой и самый используемый метод. Он запрашивает представление ресурса. GET никогда не должен изменять данные на сервере.
Получение одного ресурса
GET /users/123HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "Иван",
"email": "ivan@example.com"
}Получение коллекции
GET /usersHTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{"id": 1, "name": "Иван"},
{"id": 2, "name": "Петр"}
]
}GET с параметрами (фильтрация, пагинация, сортировка)
GET /users?status=active&page=2&limit=10&sort=-created_atЧего нельзя делать с GET
# Плохо! GET не должен изменять данные
GET /users/123/delete
GET /users/123?action=activate
GET /api/charge?amount=100&user=123Почему: GET должен быть безопасным. Поисковые роботы, браузеры, префетчеры могут случайно вызвать такие URL. Идемпотентность GET гарантирует, что повторные вызовы не навредят, но безопасность требует, чтобы они вообще ничего не меняли.
Кеширование GET
GET ответы могут кешироваться браузером, прокси, CDN.
HTTP/1.1 200 OK
Cache-Control: max-age=300
ETag: "abc123"
{"id": 123, "name": "Иван"}POST: Создание и действия
POST используется для создания новых ресурсов и для операций, которые не вписываются в стандартные CRUD.
Создание ресурса
POST /users
Content-Type: application/json
{
"name": "Иван",
"email": "ivan@example.com"
}HTTP/1.1 201 Created
Location: /users/123
Content-Type: application/json
{
"id": 123,
"name": "Иван",
"email": "ivan@example.com",
"created_at": "2024-01-15T10:30:00Z"
}Важно: POST не идемпотентен. Если отправить один и тот же запрос дважды, могут создаться два ресурса.
Выполнение действий (не-CRUD операции)
POST /users/123/activate
POST /orders/456/pay
POST /emails/send
POST /search # поиск с телом запросаКогда POST, а когда PUT
| Критерий | POST | PUT |
|---|---|---|
| Кто создаёт ID | Сервер | Клиент |
| URI | /users (коллекция) | /users/123 (конкретный) |
| Идемпотентность | Нет | Да |
| Использование | Создание, действия | Полная замена |
Как сделать POST идемпотентным
Метод 1: Idempotency-Key (заголовок)
Клиент генерирует уникальный ключ и шлёт в заголовке. Сервер запоминает ключ и результат.
Пример запроса:
POST /payments HTTP/1.1
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{ "amount": 100, "from": "alice", "to": "bob" }Логика сервера:
- Проверить в БД (или Redis) ключ
550e8400-.... - Если ключа нет → выполнить перевод, сохранить результат (например,
{ "status": "ok", "payment_id": 123 }) под этим ключом. - Если ключ есть → вернуть сохранённый результат, ничего не меняя.
Метод 2: UUID в теле (клиентский ID ресурса)
Клиент сам назначает ID ресурса и включает его в тело запроса.
Пример запроса:
POST /users HTTP/1.1
Content-Type: application/json
{ "user_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Alice" }Логика сервера:
- Проверить, есть ли пользователь с
user_id = 550e8400-.... - Если нет → создать нового пользователя с этим ID.
- Если есть → вернуть существующего (или статус 409 Conflict, в зависимости от дизайна).
PUT: Полная замена ресурса
PUT полностью заменяет ресурс по указанному URI. Если ресурс существует — заменяет. Если нет — создаёт (в некоторых API).
Полная замена ресурса
PUT /users/123
Content-Type: application/json
{
"name": "Иван Петров",
"email": "ivan.new@example.com",
"phone": "+7-999-123-45-67"
}HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "Иван Петров",
"email": "ivan.new@example.com",
"phone": "+7-999-123-45-67"
}Важное свойство: PUT требует полного представления
# Если отправить только имя
PUT /users/123
{"name": "Новое имя"}
# Что произойдёт с email, phone?
# В правильно реализованном PUT — они будут удалены (заменены на NULL)Поэтому для частичных обновлений используют PATCH.
PUT для создания (с известным ID)
PUT /users/123
Content-Type: application/json
{
"name": "Иван",
"email": "ivan@example.com"
}HTTP/1.1 201 Created
Location: /users/123PATCH: Частичное изменение ресурса
PATCH применяет частичные изменения к ресурсу. В отличие от PUT, клиент отправляет только те поля, которые нужно изменить.
Частичное обновление
PATCH /users/123
Content-Type: application/json
{
"phone": "+7-999-999-99-99"
}HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "Иван",
"email": "ivan@example.com",
"phone": "+7-999-999-99-99"
}JSON Patch (RFC 6902) — стандартный формат для PATCH
PATCH /users/123
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/phone", "value": "+7-999-999-99-99" },
{ "op": "add", "path": "/tags/-", "value": "vip" },
{ "op": "remove", "path": "/old_field" }
]PUT vs PATCH
| Аспект | PUT | PATCH |
|---|---|---|
| Что отправляет клиент | Полное представление ресурса | Только изменения |
| Что происходит с отсутствующими полями | Удаляются (или NULL) | Остаются без изменений |
| Когда использовать | Полная замена | Частичное обновление |
| Идемпотентность | Да | Не гарантирована |
DELETE: Удаление ресурса
DELETE удаляет указанный ресурс.
Удаление ресурса
DELETE /users/123HTTP/1.1 204 No Content# Или с телом ответа (информация о удалении)
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"deleted": true,
"message": "Пользователь удалён"
}DELETE для коллекций
# Удаление всех пользователей со статусом inactive
DELETE /users?status=inactive # Не стандартизировано, лучше POST /users/bulk-deleteКаскадное удаление
DELETE /users/123Вопрос: удаляются ли заказы пользователя? API должен документировать такое поведение.
HEAD и OPTIONS: Вспомогательные методы
HEAD
HEAD работает как GET, но возвращает только заголовки, без тела. Полезен для проверки существования ресурса или получения метаданных.
HEAD /users/123HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 256
Last-Modified: Wed, 15 Jan 2024 10:30:00 GMT
ETag: "abc123"Используется для:
- Проверка существования ресурса (без загрузки данных)
- Получение Content-Length (размера ответа)
- Получение Last-Modified (проверка актуальности кеша)
- Проверка доступности (health check)
OPTIONS
OPTIONS возвращает список HTTP методов, поддерживаемых для ресурса.
OPTIONS /users/123HTTP/1.1 204 No Content
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONSИспользуется для CORS preflight запросов (браузер проверяет, разрешён ли запрос с другого домена).
Безопасность и идемпотентность
Безопасные методы (Safe)
Безопасные методы не изменяют состояние сервера. Их можно вызывать сколько угодно раз — побочных эффектов не будет.
| Метод | Безопасный | Почему |
|---|---|---|
GET | Да | Только чтение |
HEAD | Да | Только заголовки |
OPTIONS | Да | Только информация о методах |
POST | Нет | Создаёт или изменяет |
PUT | Нет | Изменяет |
PATCH | Нет | Изменяет |
DELETE | Нет | Изменяет |
Почему безопасность важна: Поисковые роботы, префетчеры, браузеры могут автоматически делать GET, HEAD, OPTIONS запросы. Если эти методы изменяют данные — возможны катастрофы.
Идемпотентные методы (Idempotent)
Идемпотентный метод можно вызвать несколько раз подряд, и результат будет таким же, как от одного вызова.
| Метод | Идемпотентный | Почему |
|---|---|---|
GET | Да | Повторное чтение даёт тот же результат |
HEAD | Да | Повторное чтение даёт те же заголовки |
OPTIONS | Да | Повторный запрос даёт те же методы |
PUT | Да | Повторная полная замена ресурса даёт тот же результат |
DELETE | Да | Повторное удаление уже удалённого ресурса ничего не меняет |
POST | Нет | Повторная отправка может создать второй ресурс |
PATCH | Не гарантирована | Зависит от реализации |
Пример идемпотентности:
DELETE /users/123 # первый раз — удаляет
DELETE /users/123 # второй раз — ресурс уже удалён, но ответ тот же (404 или 204)PUT /users/123 {"name": "Иван"} # первый раз — создаёт или обновляет
PUT /users/123 {"name": "Иван"} # второй раз — результат тот жеPOST /users {"name": "Иван"} # первый раз — создаёт пользователя
POST /users {"name": "Иван"} # второй раз — может создать второго пользователяПочему идемпотентность важна: При сетевых сбоях клиент может не знать, дошёл ли запрос. Если метод идемпотентен, клиент может безопасно повторить запрос.
PUT vs POST: Практические правила
| Сценарий | Какой метод | Почему |
|---|---|---|
| Создание с автоматическим ID | POST | Клиент не знает ID |
| Создание с известным ID | PUT | Клиент знает ID, PUT идемпотентен |
| Полная замена ресурса | PUT | PUT заменяет целиком |
| Частичное обновление | PATCH | PATCH изменяет только указанные поля |
| Загрузка файла | POST | Создание нового ресурса |
| Выполнение действия | POST | Не CRUD операция |
| Поиск со сложным запросом | POST | GET с длинным телом не поддерживается |
Примеры использования методов
CRUD операции для пользователя
# Создать пользователя (сервер генерирует ID)
POST /users
# Получить пользователя
GET /users/123
# Полностью заменить пользователя
PUT /users/123
# Частично обновить пользователя
PATCH /users/123
# Удалить пользователя
DELETE /users/123Работа с коллекцией
# Получить всех пользователей
GET /users
# Создать пользователя
POST /users
# Удалить всех пользователей (редко, обычно не делают)
DELETE /users # осторожно!Действия (не CRUD)
# Активация пользователя
POST /users/123/activate
# Отправка email
POST /emails/send
# Поиск со сложным запросом (тело в GET не поддерживается везде)
POST /searchЧастые ошибки с HTTP методами
Ошибка 1: GET для изменения данных
GET /users/123/delete
GET /api/charge?amount=100Почему плохо: Поисковые роботы, префетчеры могут вызвать эти URL. GET должен быть безопасным.
Как исправить: DELETE /users/123, POST /api/charge.
Ошибка 2: POST для получения данных
POST /getUser
{"id": 123}Почему плохо: POST не кешируется. POST не идемпотентен.
Как исправить: GET /users/123.
Ошибка 3: PUT для частичного обновления
PUT /users/123
{"phone": "+7-999-123-45-67"}
# Остальные поля (name, email) удаляются или становятся NULLКак исправить: Использовать PATCH для частичного обновления.
Ошибка 4: Неправильный статус-код для POST
POST /users
→ HTTP/1.1 200 OKКак исправить: 201 Created с заголовком Location.
Ошибка 5: DELETE с телом запроса
DELETE /users
{"ids": [1, 2, 3]}DELETE по спецификации может иметь тело, но не все серверы и прокси это поддерживают.
Как исправить: POST /users/bulk-delete с телом.
Резюме для системного аналитика
HTTP методы — это глаголы REST. Они определяют действие над ресурсом. GET для чтения, POST для создания, PUT для полной замены, PATCH для частичного изменения, DELETE для удаления.
GET — безопасный и идемпотентный. Только чтение, не изменяет данные. Может кешироваться. Должен использоваться для всех операций чтения.
POST — не идемпотентный. Используется для создания ресурсов (когда ID генерирует сервер) и для любых действий, не вписывающихся в CRUD.
PUT — идемпотентный. Полностью заменяет ресурс. Клиент знает ID ресурса. Отсутствующие поля удаляются.
PATCH — частичное обновление. Изменяет только указанные поля. Не обязан быть идемпотентным, но может быть.
DELETE — идемпотентный. Удаляет ресурс. Повторный DELETE того же ресурса не должен вызывать ошибку.
Безопасность и идемпотентность — важные свойства. GET, HEAD, OPTIONS — безопасны. GET, HEAD, OPTIONS, PUT, DELETE — идемпотентны. POST — ни то, ни другое. PATCH — не гарантирует идемпотентность.