Введение
HTTP методы — это фундамент, на котором построен весь веб. Каждый раз, когда вы открываете страницу, отправляете форму, загружаете файл или удаляете аккаунт — за кулисами работает конкретный HTTP метод, который говорит серверу, что именно вы хотите сделать.
В REST API правильное использование HTTP методов — не просто конвенция, а ключевой принцип проектирования. Неправильный выбор метода ломает кэширование, нарушает безопасность и превращает API в непредсказуемый хаос.
В этой статье вы узнаете:
- ✅ Все основные HTTP методы и их назначение
- ✅ Разницу между GET и POST, PUT и PATCH
- ✅ Что такое идемпотентность и безопасность методов
- ✅ Примеры кода на JavaScript, Python и PHP
- ✅ Типичные ошибки и как их избежать
📋 Содержание
- Что такое HTTP методы
- Обзор всех методов
- GET — получение данных
- POST — создание ресурса
- PUT — полная замена ресурса
- PATCH — частичное обновление
- DELETE — удаление ресурса
- HEAD и OPTIONS
- Идемпотентность и безопасность
- CRUD-операции и HTTP методы
- Полные примеры на трёх языках
- Частые ошибки
- FAQ
- Заключение
Что такое HTTP методы
HTTP метод (HTTP verb) — это указание серверу, какое действие нужно выполнить с запрашиваемым ресурсом. Метод передаётся в первой строке HTTP-запроса:
GET /api/users HTTP/1.1
Host: example.com
Accept: application/json
Здесь GET — метод, /api/users — ресурс, к которому обращаемся.
Простая аналогия:
Представьте REST API как библиотеку. URL — это адрес книги на полке, а HTTP метод — действие, которое вы хотите с ней сделать:
- GET — взять книгу и прочитать
- POST — добавить новую книгу на полку
- PUT — заменить книгу полностью (новым изданием)
- PATCH — вклеить новую страницу
- DELETE — убрать книгу с полки
Обзор всех HTTP методов
Стандарт HTTP определяет 9 методов, но в повседневной разработке API используются 5–7 из них:
| Метод | Действие | Тело запроса | Безопасный | Идемпотентный |
|---|---|---|---|---|
| GET | Получить данные | Нет | ✓ Да | ✓ Да |
| POST | Создать ресурс | Да | ✗ Нет | ✗ Нет |
| PUT | Заменить ресурс целиком | Да | ✗ Нет | ✓ Да |
| PATCH | Обновить часть ресурса | Да | ✗ Нет | ✗ Нет |
| DELETE | Удалить ресурс | Нет (обычно) | ✗ Нет | ✓ Да |
| HEAD | Получить заголовки (без тела) | Нет | ✓ Да | ✓ Да |
| Узнать доступные методы | Нет | ✓ Да | ✓ Да |
GET — получение данных
GET — самый распространённый HTTP метод. Используется для получения данных с сервера без их изменения.
Характеристики GET
- Безопасный — не изменяет данные на сервере
- Идемпотентный — повторный вызов даёт тот же результат
- Кэшируемый — браузер и прокси могут кэшировать ответ
- Без тела запроса — параметры передаются в URL (query string)
Примеры запросов
// Получить список пользователей
fetch('/api/users')
.then(res => res.json())
.then(users => console.log(users));
// GET /api/users → 200 OK
// Получить одного пользователя по ID
fetch('/api/users/42')
.then(res => res.json())
.then(user => console.log(user));
// GET /api/users/42 → 200 OK
// Фильтрация и пагинация через query string
fetch('/api/users?role=admin&page=2&limit=10')
.then(res => res.json())
.then(data => console.log(data));
// GET /api/users?role=admin&page=2&limit=10 → 200 OK
Типичные ответы
| Код | Ситуация |
|---|---|
200 OK |
Данные найдены и возвращены |
304 Not Modified |
Данные не изменились (используйте кэш) |
404 Not Found |
Ресурс не найден |
⚠️ GET никогда не должен изменять данные!
Антипаттерн: GET /api/users/42/delete — удаление через GET. Браузерный prefetch, поисковый краулер или кэширующий прокси может случайно вызвать этот URL и удалить данные. Для изменений используйте POST, PUT, PATCH или DELETE.
POST — создание ресурса
POST используется для создания нового ресурса на сервере. Данные передаются в теле запроса.
Характеристики POST
- Небезопасный — изменяет данные на сервере
- Не идемпотентный — повторный вызов создаёт ещё один ресурс
- Не кэшируемый — браузер не кэширует POST-запросы
- С телом запроса — данные передаются в body (JSON, form-data)
Примеры запросов
// Создать нового пользователя
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Иван Петров',
email: 'ivan@example.com',
role: 'developer'
})
});
if (response.status === 201) {
const newUser = await response.json();
console.log('Создан:', newUser);
// Заголовок Location: /api/users/43
console.log('URL:', response.headers.get('Location'));
}
// Загрузить файл
const formData = new FormData();
formData.append('avatar', fileInput.files[0]);
await fetch('/api/users/42/avatar', {
method: 'POST',
body: formData
});
Типичные ответы
| Код | Ситуация |
|---|---|
201 Created |
Ресурс успешно создан (+ заголовок Location) |
400 Bad Request |
Невалидные данные в запросе |
409 Conflict |
Ресурс уже существует (дублирование email) |
422 Unprocessable Entity |
Данные не прошли валидацию |
⚠️ POST не идемпотентен!
Если пользователь случайно нажмёт «Отправить» дважды — создадутся два одинаковых ресурса. Для защиты используйте idempotency key — уникальный идентификатор запроса в заголовке:
fetch('/api/payments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': 'unique-request-id-12345'
},
body: JSON.stringify({ amount: 1000 })
});
PUT — полная замена ресурса
PUT заменяет ресурс целиком. Вы должны передать все поля объекта, даже те, которые не изменились.
Характеристики PUT
- Идемпотентный — повторный вызов с теми же данными даёт тот же результат
- Требует полного представления — все поля объекта
- Создаёт или заменяет — если ресурс не существует, PUT может его создать
Примеры запросов
// Полностью заменить данные пользователя
const response = await fetch('/api/users/42', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Иван Сидоров', // изменили
email: 'ivan.new@example.com', // изменили
role: 'developer', // не изменился, но НУЖНО передать
age: 30 // не изменился, но НУЖНО передать
})
});
// 200 OK — ресурс обновлён
// 201 Created — ресурс не существовал и был создан
PUT — это «заменить файл целиком»
Если вы не передадите поле age, сервер должен его обнулить или удалить. PUT — это не «обновить поля», а «вот как теперь должен выглядеть этот ресурс целиком».
PATCH — частичное обновление
PATCH обновляет только указанные поля ресурса. Передаёте только то, что хотите изменить.
Характеристики PATCH
- Частичное обновление — только изменённые поля
- Не идемпотентный (формально) — хотя на практике часто ведёт себя идемпотентно
- Экономит трафик — не нужно передавать весь объект
Примеры запросов
// Обновить только email пользователя
const response = await fetch('/api/users/42', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'new.email@example.com'
// Остальные поля НЕ передаём — они не изменятся
})
});
// 200 OK — ресурс обновлён, возвращается полный объект
PUT vs PATCH — разница на примере
Допустим, у нас есть пользователь:
{
"id": 42,
"name": "Иван",
"email": "ivan@example.com",
"age": 30
}
Хотим изменить только email:
✅ PATCH (правильно для частичного обновления)
// Передаём ТОЛЬКО изменённое поле
PATCH /api/users/42
{ "email": "new@example.com" }
// Результат: age и name сохранены
{
"id": 42,
"name": "Иван",
"email": "new@example.com",
"age": 30
}
❌ PUT (нужно передать ВСЁ)
// Должны передать ВСЕ поля
PUT /api/users/42
{
"name": "Иван",
"email": "new@example.com",
"age": 30
}
// Если забудем age:
PUT /api/users/42
{ "email": "new@example.com" }
// age станет null!
DELETE — удаление ресурса
DELETE удаляет ресурс на сервере.
Характеристики DELETE
- Идемпотентный — повторное удаление не создаёт ошибку (или возвращает 404)
- Обычно без тела запроса — ресурс идентифицируется по URL
- Может быть soft или hard delete — пометить как удалённый или удалить физически
Примеры запросов
// Удалить пользователя
const response = await fetch('/api/users/42', {
method: 'DELETE',
headers: {
'Authorization': 'Bearer eyJhbGciOi...'
}
});
if (response.status === 204) {
console.log('Пользователь удалён');
}
if (response.status === 404) {
console.log('Пользователь не найден');
}
Типичные ответы
| Код | Ситуация |
|---|---|
204 No Content |
Ресурс удалён, тело ответа пустое |
200 OK |
Ресурс удалён, возвращается удалённый объект |
404 Not Found |
Ресурс не найден (уже удалён?) |
403 Forbidden |
Нет прав на удаление |
⚠️ Soft Delete vs Hard Delete
Soft delete — помечаем запись как удалённую (поле deleted_at), но не удаляем из БД. Позволяет восстановить данные. Используется в большинстве production-систем.
Hard delete — физическое удаление из базы данных. Необратимо. Используйте только когда уверены, что данные не понадобятся (GDPR, очистка временных данных).
HEAD и OPTIONS
HEAD — получить только заголовки
HEAD идентичен GET, но сервер возвращает только заголовки без тела ответа. Полезен для:
- Проверки существования ресурса без загрузки данных
- Определения размера файла перед скачиванием (
Content-Length) - Проверки актуальности кэша (
Last-Modified,ETag)
// Проверить размер файла перед скачиванием
const response = await fetch('/api/files/report.pdf', {
method: 'HEAD'
});
const fileSize = response.headers.get('Content-Length');
console.log(`Размер файла: ${(fileSize / 1024 / 1024).toFixed(2)} МБ`);
// Проверить, существует ли ресурс
const exists = response.status === 200;
— узнать доступные методы
OPTIONS возвращает информацию о доступных методах для данного URL. Активно используется в CORS preflight-запросах — браузер автоматически отправляет OPTIONS перед кросс-доменными запросами.
OPTIONS /api/users HTTP/1.1
Host: api.example.com
HTTP/1.1 204 No Content
Allow: GET, POST, OPTIONS
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Идемпотентность и безопасность
Два ключевых свойства HTTP методов, которые влияют на архитектуру API, кэширование и обработку ошибок.
Безопасные методы (Safe)
Безопасный метод не изменяет состояние сервера. Его можно вызывать сколько угодно раз без побочных эффектов.
Безопасные: GET, HEAD, OPTIONS
Браузеры, поисковые роботы и CDN могут свободно вызывать безопасные методы. Именно поэтому GET не должен изменять данные — поисковый робот Google может зайти на GET /api/users/42/delete и случайно удалить пользователя.
Идемпотентные методы (Idempotent)
Идемпотентный метод при повторном вызове с теми же данными даёт тот же результат. Состояние сервера после N вызовов то же, что и после одного.
| Метод | Идемпотентен? | Объяснение |
|---|---|---|
| GET | ✓ Да | Чтение не меняет данные — сколько ни читай, результат один |
| POST | ✗ Нет | Каждый вызов создаёт новый ресурс (2 вызова = 2 пользователя) |
| PUT | ✓ Да | Замена одним и тем же объектом — результат один и тот же |
| PATCH | ✗ Нет* | Формально нет (зависит от реализации), но на практике обычно да |
| DELETE | ✓ Да | Удалить дважды — ресурс всё равно удалён (второй вызов = 404) |
⚠️ Почему идемпотентность важна?
Сеть ненадёжна. Если запрос «завис» и клиент не получил ответ — нужно повторить. С идемпотентным методом повторный вызов безопасен. С POST — может создать дубликат.
Пример: Клиент отправил PUT /api/users/42, сервер обработал, но ответ потерялся в сети. Клиент повторяет запрос — результат тот же, данные не испорчены. С POST /api/payments без idempotency key — деньги спишутся дважды.
CRUD-операции и HTTP методы
CRUD (Create, Read, Update, Delete) — четыре базовых операции с данными. Каждая операция соответствует HTTP методу:
| CRUD | HTTP метод | URL (пример) | Описание |
|---|---|---|---|
| Create | POST | POST /api/users |
Создать нового пользователя |
| Read (список) | GET | GET /api/users |
Получить список пользователей |
| Read (один) | GET | GET /api/users/42 |
Получить пользователя #42 |
| Update (полностью) | PUT | PUT /api/users/42 |
Заменить пользователя #42 целиком |
| Update (частично) | PATCH | PATCH /api/users/42 |
Обновить поля пользователя #42 |
| Delete | DELETE | DELETE /api/users/42 |
Удалить пользователя #42 |
Правила именования URL в REST API:
- Используйте существительные, а не глаголы:
/api/users, не/api/getUsers - Коллекция во множественном числе:
/api/users, не/api/user - Вложенные ресурсы:
/api/users/42/posts— посты пользователя 42 - Действие определяет HTTP метод, а не URL
Полные примеры на трёх языках
JavaScript (fetch API)
const API_BASE = 'https://api.example.com';
// GET — получить список
async function getUsers() {
const res = await fetch(`${API_BASE}/users`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
// POST — создать
async function createUser(userData) {
const res = await fetch(`${API_BASE}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
if (res.status !== 201) throw new Error(`HTTP ${res.status}`);
return res.json();
}
// PUT — заменить целиком
async function replaceUser(id, userData) {
const res = await fetch(`${API_BASE}/users/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
// PATCH — обновить частично
async function updateUser(id, fields) {
const res = await fetch(`${API_BASE}/users/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(fields)
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
// DELETE — удалить
async function deleteUser(id) {
const res = await fetch(`${API_BASE}/users/${id}`, {
method: 'DELETE'
});
if (res.status !== 204) throw new Error(`HTTP ${res.status}`);
return true;
}
// Использование
const users = await getUsers();
const newUser = await createUser({ name: 'Иван', email: 'ivan@test.ru' });
await updateUser(newUser.id, { email: 'new@test.ru' });
await deleteUser(newUser.id);
Python (requests)
import requests
API_BASE = 'https://api.example.com'
headers = {'Content-Type': 'application/json'}
# GET — получить список
response = requests.get(f'{API_BASE}/users')
users = response.json()
# POST — создать
response = requests.post(f'{API_BASE}/users', json={
'name': 'Иван',
'email': 'ivan@test.ru'
}, headers=headers)
if response.status_code == 201:
new_user = response.json()
print(f'Создан: {new_user["id"]}')
# PUT — заменить целиком
response = requests.put(f'{API_BASE}/users/42', json={
'name': 'Иван Сидоров',
'email': 'ivan@test.ru',
'role': 'admin'
}, headers=headers)
# PATCH — обновить частично
response = requests.patch(f'{API_BASE}/users/42', json={
'email': 'new@test.ru'
}, headers=headers)
# DELETE — удалить
response = requests.delete(f'{API_BASE}/users/42')
if response.status_code == 204:
print('Удалено')
PHP (cURL)
<?php
$apiBase = 'https://api.example.com';
function apiRequest(string $url, string $method = 'GET', ?array $data = null): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
}
$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'status' => $statusCode,
'data' => json_decode($response, true),
];
}
// GET — список
$result = apiRequest("$apiBase/users");
$users = $result['data'];
// POST — создать
$result = apiRequest("$apiBase/users", 'POST', [
'name' => 'Иван',
'email' => 'ivan@test.ru',
]);
// $result['status'] === 201
// PUT — заменить
$result = apiRequest("$apiBase/users/42", 'PUT', [
'name' => 'Иван Сидоров',
'email' => 'ivan@test.ru',
'role' => 'admin',
]);
// PATCH — обновить
$result = apiRequest("$apiBase/users/42", 'PATCH', [
'email' => 'new@test.ru',
]);
// DELETE — удалить
$result = apiRequest("$apiBase/users/42", 'DELETE');
// $result['status'] === 204
Частые ошибки
❌ Ошибка 1: Использовать GET для изменения данных
GET /api/users/42/delete ← НИКОГДА так не делайте
GET /api/users/42/activate ← тоже плохо
GET /api/send-email?to=... ← и так нельзя
GET должен только читать. Для действий используйте POST или соответствующий метод.
❌ Ошибка 2: POST для всего
Некоторые API используют только POST для всех операций, передавая действие в теле запроса. Это нарушает принципы REST и ломает кэширование, семантику и инструменты мониторинга.
POST /api → { "action": "getUser", "id": 42 } ← RPC, не REST
POST /api → { "action": "deleteUser", "id": 42 } ← антипаттерн
❌ Ошибка 3: PUT вместо PATCH для частичного обновления
Если нужно обновить одно поле — используйте PATCH. PUT с неполным набором полей обнулит пропущенные значения.
✅ Правильный выбор метода — шпаргалка
| Нужно | Метод | Пример |
|---|---|---|
| Получить данные | GET | GET /api/users |
| Создать новый ресурс | POST | POST /api/users |
| Заменить ресурс целиком | PUT | PUT /api/users/42 |
| Обновить пару полей | PATCH | PATCH /api/users/42 |
| Удалить ресурс | DELETE | DELETE /api/users/42 |
| Проверить заголовки | HEAD | HEAD /api/files/report.pdf |
| Узнать разрешённые методы | OPTIONS /api/users |
FAQ
❓ В чём разница между PUT и PATCH?
Ответ: PUT заменяет ресурс целиком — вы должны передать все поля объекта. Если пропустите поле — оно будет обнулено. PATCH обновляет только указанные поля — остальные остаются без изменений.
PUT /users/42 → { name, email, age } — ВСЕ поля обязательны
PATCH /users/42 → { email } — только изменённое поле
❓ Почему GET-запросы не должны изменять данные?
Ответ: GET — безопасный метод по спецификации HTTP. Браузеры, прокси и CDN могут кэшировать GET-запросы и повторять их автоматически. Если GET будет удалять или изменять данные, то:
- Поисковый робот Google, обходя ваш API, может случайно стереть данные
- Браузерный prefetch загрузит URL до клика — и выполнит действие
- Кэширующий прокси может повторить запрос
❓ Что такое идемпотентность HTTP методов?
Ответ: Идемпотентный метод — тот, который при повторном вызове с теми же параметрами даёт тот же результат. Состояние сервера не меняется от повторных вызовов.
- GET — идемпотентен: сколько ни читай, результат один
- PUT — идемпотентен: замена тем же объектом = тот же результат
- DELETE — идемпотентен: удалить дважды = ресурс всё равно удалён
- POST — НЕ идемпотентен: 2 вызова = 2 новых ресурса
❓ Когда использовать POST вместо PUT?
Ответ:
- POST — когда сервер сам назначает идентификатор нового ресурса:
POST /api/users→ сервер создаёт пользователя сid=42 - PUT — когда клиент знает идентификатор и хочет создать или заменить конкретный ресурс:
PUT /api/users/42→ создать/заменить пользователя сid=42
POST /api/users → Создай пользователя (ID назначит сервер)
PUT /api/users/42 → Создай/замени пользователя #42 (ID знает клиент)
Заключение
📝 Сводная таблица HTTP методов
| Метод | CRUD | Тело | Идемпотентный | Типичный ответ |
|---|---|---|---|---|
| GET | Read | Нет | ✓ | 200 OK |
| POST | Create | Да | ✗ | 201 Created |
| PUT | Replace | Да | ✓ | 200 OK |
| PATCH | Update | Да | ✗* | 200 OK |
| DELETE | Delete | Нет | ✓ | 204 No Content |
Правильное использование HTTP методов — это не педантизм, а фундамент надёжного API. Когда каждый метод используется по назначению, API становится предсказуемым, кэшируемым, безопасным и удобным для клиентов.
💡 Три главных правила:
- GET — только для чтения, никогда не изменяйте данные через GET
- PATCH для частичного обновления, PUT — только когда нужно заменить ресурс целиком
- Помните об идемпотентности — для POST используйте idempotency key, чтобы избежать дубликатов
🎯 Тестируйте все HTTP методы с LightBox API
Хотите проверить, как ваше приложение работает с GET, POST, PUT, PATCH и DELETE?
LightBox API позволяет создать Mock API с поддержкой всех HTTP методов — настройте разные ответы для каждого метода и тестируйте CRUD-операции.
- ✓ Поддержка всех HTTP методов (GET, POST, PUT, PATCH, DELETE)
- ✓ Разные статус коды и тела ответов
- ✓ Логирование всех входящих запросов
- ✓ Импорт Swagger/OpenAPI за 2 минуты
Статья опубликована: 23 февраля 2026
Автор: LightBox API Team