Методи HTTP та їхня семантика
Методи HTTP та їхня семантика
🎯 Мета розділу
- Опанувати семантику стандартних методів HTTP (GET, POST, PUT, PATCH, DELETE та інших).
- Зрозуміти концепції безпечності (safe), ідемпотентності (idempotent) та кешованості методів.
- Навчитися правильно обирати метод для конкретного сценарію взаємодії з API.
- Засвоїти різницю між PUT та PATCH, GET та POST у контексті RESTful архітектури.
🔑 Ключові терміни
- HTTP Method — дієслово, що визначає операцію над ресурсом (GET, POST, PUT, DELETE тощо).
- Safe Method — метод, що не змінює стан ресурсу на сервері (лише читання).
- Idempotent Method — метод, повторний виклик якого дає той самий результат, що й одиночний.
- Cacheable — метод, відповідь на який може бути закешована клієнтом або проксі.
- Request Body — можливість методу передавати дані у тілі запиту.
Фундаментальні властивості HTTP-методів
Перш ніж розглядати окремі методи, необхідно зрозуміти три ключові характеристики, які визначають поведінку методу та його призначення у архітектурі HTTP:
Безпечність (Safe Methods)
Безпечний метод (safe method) — це метод, який не змінює стан ресурсу на сервері. Безпечні методи призначені виключно для отримання інформації без будь-яких побічних ефектів (створення, оновлення, видалення даних).
Згідно зі специфікацією RFC 7231, безпечні методи повинні мати семантику «лише читання» (read-only). Це означає, що клієнт може повторювати безпечні запити необмежену кількість разів без ризику порушити цілісність даних на сервері.
Безпечні методи: GET, HEAD, OPTIONS, TRACE.
Небезпечні методи: POST, PUT, PATCH, DELETE, CONNECT.
Ідемпотентність (Idempotent Methods)
Ідемпотентний метод (idempotent method) — це метод, повторне виконання якого N разів дає той самий результат, що й одноразове виконання (де N ≥ 1).
Формальне визначення ідемпотентності:
Завантаження...Для HTTP це означає: якщо клієнт надсилає ідентичний запит двічі, стан сервера після другого запиту буде таким самим, як після першого.
Ідемпотентні методи: GET, HEAD, PUT, DELETE, OPTIONS, TRACE.
Неідемпотентні методи: POST, PATCH (частково — залежить від реалізації).
Практичне значення ідемпотентності:
Ідемпотентність дозволяє клієнту безпечно повторювати запити у разі мережевих збоїв, таймаутів або невизначених відповідей. Якщо клієнт надіслав PUT-запит, але не отримав відповідь через розрив з'єднання, він може повторити запит без ризику дублювання операції.
Приклад:
DELETE /api/v1/users/42 HTTP/1.1
- Перший виклик: Користувач з ID 42 видаляється, сервер повертає
204 No Content. - Другий виклик: Користувача вже немає, але сервер все одно повертає
204 No Content(або404 Not Found). Стан сервера не змінюється, оскільки користувач вже відсутній.
Контрприклад (POST — неідемпотентний):
POST /api/v1/orders HTTP/1.1
Content-Type: application/json
{"productId": 123, "quantity": 2}
- Перший виклик: Створюється замовлення з ID 1001.
- Другий виклик: Створюється нове замовлення з ID 1002 (дублікат).
Повторне виконання POST змінює стан сервера, створюючи новий ресурс кожного разу.
Кешованість (Cacheable Methods)
Кешованість (cacheability) визначає, чи може відповідь на запит даного методу бути збережена у кеші (браузер, CDN, проксі-сервер) для повторного використання без звернення до сервера.
Кешовані методи: GET, HEAD, POST (рідко кешується на практиці).
Некешовані методи: PUT, PATCH, DELETE, OPTIONS, TRACE, CONNECT.
Кешування дозволяє значно зменшити навантаження на сервер та прискорити відповіді для користувачів, оскільки дані можуть бути отримані з локального кешу або проксі замість повторного запиту до origin-сервера.
Порівняльна таблиця HTTP-методів
Для систематизації знань наведемо порівняльну таблицю характеристик всіх стандартних методів HTTP/1.1:
| Метод | Безпечний | Ідемпотентний | Кешований | Має тіло запиту | Має тіло відповіді | Типове використання |
|---|---|---|---|---|---|---|
| GET | ✅ | ✅ | ✅ | ❌ (рідко) | ✅ | Отримання ресурсу |
| QUERY | ✅ | ✅ | ⚠️ (складно) | ✅ | ✅ | Складний пошук з фільтрами у тілі |
| HEAD | ✅ | ✅ | ✅ | ❌ | ❌ | Отримання метаданих без тіла |
| POST | ❌ | ❌ | ⚠️ (теоретично) | ✅ | ✅ | Створення ресурсу, відправка даних |
| PUT | ❌ | ✅ | ❌ | ✅ | ✅ | Повна заміна ресурсу |
| PATCH | ❌ | ⚠️ (залежить) | ❌ | ✅ | ✅ | Часткове оновлення ресурсу |
| DELETE | ❌ | ✅ | ❌ | ❌ (опційно) | ✅ (опційно) | Видалення ресурсу |
| OPTIONS | ✅ | ✅ | ❌ | ❌ | ✅ | Запит можливостей сервера |
| TRACE | ✅ | ✅ | ❌ | ❌ | ✅ | Діагностика (луна-запит) |
| CONNECT | ❌ | ❌ | ❌ | ❌ | ✅ | Встановлення тунелю (HTTPS через проксі) |
Детальний розгляд HTTP-методів
GET — Отримання ресурсу
Призначення: Запит представлення ресурсу без зміни його стану. GET є найпоширенішим методом у вебі — саме він використовується браузером при переході за посиланням або введенні URL в адресний рядок.
Характеристики:
- ✅ Безпечний (лише читання)
- ✅ Ідемпотентний (повторні запити повертають той самий результат)
- ✅ Кешований
- ❌ Не повинен мати тіло запиту (технічно можливе, але не рекомендується)
Семантика: «Дай мені поточне представлення ресурсу за цією адресою».
Приклад 1: Отримання одного ресурсу
Запит:
GET /api/v1/users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGc...token...
Відповідь:
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 11:00:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 156
Cache-Control: private, max-age=300
ETag: "user-42-v5"
{"id":42,"email":"john@example.com","firstName":"John","lastName":"Doe","role":"user","createdAt":"2026-01-15T08:30:00Z"}
Приклад 2: Отримання колекції з фільтрацією та пагінацією
Запит:
GET /api/v1/products?category=laptops&price_max=50000&sort=price&order=asc&page=2&limit=10 HTTP/1.1
Host: shop.example.com
Accept: application/json
Accept-Encoding: gzip, br
Відповідь:
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 11:05:00 GMT
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Cache-Control: public, max-age=600
Content-Length: 487
{"products":[{"id":15,"name":"Acer Aspire 5","price":22000},{"id":23,"name":"HP Pavilion 15","price":28000}],"pagination":{"page":2,"limit":10,"total":47,"totalPages":5}}
Приклад 3: Пошук через query string
Запит:
GET /api/v1/users/search?q=john&role=admin&status=active HTTP/1.1
Host: api.example.com
Accept: application/json
Відповідь:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 234
{"results":[{"id":12,"email":"john.admin@example.com","role":"admin"},{"id":42,"email":"john.smith@example.com","role":"admin"}],"count":2}
GET є безпечним методом, що означає семантику «лише читання». Порушення цього принципу призводить до серйозних проблем:
- Кешування: Відповіді на GET-запити кешуються браузерами, проксі та CDN. Якщо GET змінює стан (наприклад, видаляє користувача), повторне використання кешованої відповіді приховає реальну зміну стану.
- Prefetching: Браузери та пошукові роботи можуть автоматично виконувати GET-запити для прискорення завантаження сторінок або індексації. Якщо GET має побічні ефекти, це призведе до непередбачуваних змін даних.
- Безпека: GET-запити логуються у повних URL (включно з параметрами) у логах сервера, проксі, історії браузера. Якщо GET змінює дані, це створює вразливість до атак через підробку URL.
Приклад небезпечного дизайну (⚠️ НЕ РОБІТЬ ТАК):
GET /api/v1/users/42/delete HTTP/1.1
Якщо користувач випадково перейде за цим посиланням або пошуковий робот його проіндексує — користувач буде видалений.
Правильний підхід:
DELETE /api/v1/users/42 HTTP/1.1
QUERY — Пошук з тілом запиту (експериментальний метод)
Призначення: Виконання складних пошукових запитів з можливістю передачі структурованих критеріїв пошуку через тіло запиту. Метод QUERY був запропонований у чернетці специфікації draft-ietf-httpbis-safe-method-w-body для вирішення обмежень GET при роботі зі складними фільтрами.
Характеристики:
- ✅ Безпечний (лише читання, аналогічно GET)
- ✅ Ідемпотентний
- ✅ Теоретично кешований (хоча складніше, ніж GET)
- ✅ Має тіло запиту (ключова відмінність від GET)
- ✅ Має тіло відповіді
Семантика: «Виконай пошук за складними критеріями, переданими у тілі запиту, але не змінюй стан сервера».
Проблема, яку вирішує QUERY
Традиційно GET використовується для пошуку та фільтрації через query string у URL:
GET /api/v1/products?category=laptops&price_min=20000&price_max=50000&brands=dell&brands=hp&sort=price&order=asc HTTP/1.1
Проте цей підхід має обмеження:
- Довжина URL обмежена: Більшість серверів обмежують довжину URL (зазвичай 2048–8192 символів). Складні фільтри з багатьма параметрами можуть перевищити цей ліміт.
- Складність структурованих даних: Передача вкладених об'єктів, масивів, логічних виразів через query string є незручною та нечитабельною.
- Конфіденційність: Query string логується у всіх проміжних системах (проксі, балансувальники, логи сервера), що може бути небезпечним для чутливих критеріїв пошуку.
- Кешування: URL з великою кількістю параметрів погано кешуються.
QUERY дозволяє передавати складні критерії пошуку через JSON у тілі запиту, зберігаючи семантику безпечного методу (лише читання).
Приклад 1: Складний пошук товарів
Запит GET (традиційний підхід — незручний):
GET /api/v1/products?category=laptops&price_min=20000&price_max=50000&brands=dell&brands=hp&brands=lenovo&features=ssd&features=touchscreen&ram_min=16&screen_size_min=15&in_stock=true&sort=price&order=asc&page=1&limit=20 HTTP/1.1
Host: shop.example.com
Запит QUERY (сучасний підхід — зручний):
QUERY /api/v1/products HTTP/1.1
Host: shop.example.com
Content-Type: application/json; charset=utf-8
Accept: application/json
Content-Length: 287
{
"filters": {
"category": "laptops",
"price": {"min": 20000, "max": 50000},
"brands": ["dell", "hp", "lenovo"],
"features": {
"all": ["ssd", "touchscreen"]
},
"specs": {
"ram": {"min": 16},
"screenSize": {"min": 15}
},
"inStock": true
},
"sort": {"field": "price", "order": "asc"},
"pagination": {"page": 1, "limit": 20}
}
Відповідь:
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 12:20:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 1245
Cache-Control: public, max-age=300
{
"products": [
{
"id": 15,
"name": "Dell XPS 15",
"price": 45000,
"brand": "dell",
"features": ["ssd", "touchscreen"],
"specs": {"ram": 16, "screenSize": 15.6}
},
{
"id": 23,
"name": "HP Spectre x360",
"price": 48000,
"brand": "hp",
"features": ["ssd", "touchscreen"],
"specs": {"ram": 32, "screenSize": 15.6}
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 47,
"totalPages": 3
}
}
Приклад 2: Пошук користувачів з логічними операторами
Запит:
QUERY /api/v1/users/search HTTP/1.1
Host: api.example.com
Content-Type: application/json; charset=utf-8
Authorization: Bearer eyJhbGc...admin-token...
Content-Length: 234
{
"filters": {
"or": [
{"role": "admin"},
{"permissions": {"contains": "manage_users"}}
],
"status": "active",
"createdAt": {
"gte": "2026-01-01T00:00:00Z",
"lte": "2026-12-31T23:59:59Z"
}
},
"projection": ["id", "email", "role", "lastLoginAt"],
"sort": {"field": "lastLoginAt", "order": "desc"},
"limit": 50
}
Пояснення запиту:
- Шукати користувачів, які або мають роль
admin, або мають дозвілmanage_users. - І мають статус
active. - І були створені у 2026 році.
- Повернути лише поля
id,email,role,lastLoginAt. - Відсортувати за останнім входом (найновіші спочатку).
Відповідь:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 567
{
"results": [
{
"id": 1,
"email": "admin@example.com",
"role": "admin",
"lastLoginAt": "2026-08-29T10:30:00Z"
},
{
"id": 12,
"email": "manager@example.com",
"role": "user",
"lastLoginAt": "2026-08-28T15:20:00Z"
}
],
"count": 2
}
Приклад 3: Агрегація та групування (аналітика)
Запит:
QUERY /api/v1/orders/analytics HTTP/1.1
Host: analytics.example.com
Content-Type: application/json; charset=utf-8
Authorization: Bearer eyJhbGc...token...
Content-Length: 198
{
"filters": {
"status": "completed",
"createdAt": {
"gte": "2026-08-01T00:00:00Z",
"lte": "2026-08-31T23:59:59Z"
}
},
"aggregations": {
"totalRevenue": {"sum": "totalAmount"},
"avgOrderValue": {"avg": "totalAmount"},
"orderCount": {"count": "*"}
},
"groupBy": ["paymentMethod"]
}
Відповідь:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 289
{
"results": [
{
"paymentMethod": "credit_card",
"totalRevenue": 1250000,
"avgOrderValue": 3125,
"orderCount": 400
},
{
"paymentMethod": "paypal",
"totalRevenue": 580000,
"avgOrderValue": 2900,
"orderCount": 200
}
]
}
Переваги QUERY над GET
- Необмежена складність: Можна передавати глибоко вкладені фільтри, логічні оператори (AND, OR, NOT), діапазони дат, масиви значень.
- Читабельність: JSON у тілі є структурованішим та зрозумілішим, ніж довгий query string.
- Безпека: Критерії пошуку не логуються у URL, що зменшує ризик витоку чутливої інформації.
- Гнучкість типізації: JSON підтримує булеві значення, числа, null, вкладені об'єкти без необхідності кодування.
- Відсутність обмежень довжини URL: Немає ризику перевищити ліміт довжини URL.
Обмеження та підтримка QUERY
- Більшість стандартних веб-серверів (nginx, Apache) не розпізнають метод QUERY за замовчуванням і можуть повернути
405 Method Not Allowedабо501 Not Implemented. - Для використання QUERY потрібна явна підтримка на рівні фреймворку застосунку (NestJS, Express, FastAPI тощо).
- Кешування QUERY є складнішим, оскільки кеш-ключ повинен включати хеш тіла запиту, а не лише URL.
Через обмежену підтримку QUERY багато API використовують POST для пошуку як компроміс:
POST /api/v1/products/search HTTP/1.1
Content-Type: application/json
{...складні критерії пошуку...}
Якщо ваше API вимагає складних пошукових запитів:
- Ідеально: Впровадити підтримку методу QUERY, якщо ваш фреймворк це дозволяє.
- Компроміс: Використовувати
POST /resource/searchз чітким документуванням, що це безпечна операція пошуку. - Традиційно: Використовувати GET з обмеженими фільтрами через query string для простих запитів.
HEAD — Отримання метаданих без тіла
Призначення: Аналогічний до GET, але сервер не повертає тіло відповіді — лише статусний рядок та заголовки. Використовується для перевірки існування ресурсу, отримання метаданих (розмір, тип, час модифікації) без завантаження самого вмісту.
Характеристики:
- ✅ Безпечний
- ✅ Ідемпотентний
- ✅ Кешований
- ❌ Не має тіла запиту
- ❌ Не повертає тіло відповіді (лише заголовки)
Семантика: «Дай мені метадані про ресурс, але не сам вміст».
Приклад 1: Перевірка існування файлу
Запит:
HEAD /downloads/large-file.zip HTTP/1.1
Host: cdn.example.com
Відповідь:
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 11:10:00 GMT
Content-Type: application/zip
Content-Length: 524288000
Last-Modified: Fri, 28 Aug 2026 10:00:00 GMT
ETag: "file-v3-hash789"
Accept-Ranges: bytes
Тіло відповіді порожнє, але клієнт дізнався, що файл існує (200 OK), має розмір 524 МБ (Content-Length) та підтримує часткове завантаження (Accept-Ranges: bytes).
Приклад 2: Перевірка актуальності ресурсу
Запит:
HEAD /api/v1/products/42 HTTP/1.1
Host: api.example.com
If-None-Match: "product-v2-hash456"
Відповідь (ресурс не змінився):
HTTP/1.1 304 Not Modified
ETag: "product-v2-hash456"
Cache-Control: public, max-age=3600
Відповідь (ресурс оновився):
HTTP/1.1 200 OK
ETag: "product-v3-hash789"
Content-Type: application/json
Content-Length: 256
Last-Modified: Sat, 29 Aug 2026 09:00:00 GMT
Практичне застосування HEAD:
- Перевірка доступності ресурсу перед завантаженням великого файлу.
- Моніторинг сервісів (health checks) без навантаження на генерацію відповіді.
- Валідація посилань (link checkers) — перевірка, чи повертає URL 404 без завантаження контенту.
POST — Створення ресурсу та відправка даних
Призначення: Відправка даних на сервер для обробки. Найчастіше використовується для створення нових ресурсів, але також може виконувати інші дії (відправка форми, виконання операції, додавання до колекції).
Характеристики:
- ❌ Небезпечний (змінює стан сервера)
- ❌ Неідемпотентний (повторні запити створюють нові ресурси)
- ⚠️ Теоретично кешований, але на практиці ніколи
- ✅ Має тіло запиту
- ✅ Має тіло відповіді
Семантика: «Обробіти ці дані та створити новий ресурс» або «Виконати цю операцію».
Приклад 1: Створення нового користувача
Запит:
POST /api/v1/users HTTP/1.1
Host: api.example.com
Content-Type: application/json; charset=utf-8
Authorization: Bearer eyJhbGc...token...
Content-Length: 112
{"email":"jane.doe@example.com","password":"SecurePass123!","firstName":"Jane","lastName":"Doe","role":"user"}
Відповідь (успіх):
HTTP/1.1 201 Created
Date: Sat, 29 Aug 2026 11:15:00 GMT
Location: https://api.example.com/api/v1/users/125
Content-Type: application/json; charset=utf-8
Content-Length: 178
{"id":125,"email":"jane.doe@example.com","firstName":"Jane","lastName":"Doe","role":"user","createdAt":"2026-08-29T11:15:00Z","isActive":true}
Ключові моменти:
- Код статусу 201 Created вказує на успішне створення ресурсу.
- Заголовок Location містить URI новоствореного ресурсу.
- Тіло відповіді містить повне представлення створеного ресурсу (включно з автоматично згенерованими полями
id,createdAt).
Приклад 2: Відправка форми логіну
Запит:
POST /api/v1/auth/login HTTP/1.1
Host: secure.example.com
Content-Type: application/json; charset=utf-8
Content-Length: 68
{"email":"admin@example.com","password":"MySecurePassword2026!"}
Відповідь (успіх):
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 11:20:00 GMT
Content-Type: application/json; charset=utf-8
Set-Cookie: sessionId=xyz789abc; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
Content-Length: 245
{"user":{"id":1,"email":"admin@example.com","role":"admin"},"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","expiresIn":3600}
Відповідь (помилка автентифікації):
HTTP/1.1 401 Unauthorized
Date: Sat, 29 Aug 2026 11:22:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 98
{"error":"Authentication Failed","message":"Invalid email or password. Please check your credentials."}
Приклад 3: Відправка форми з файлом (multipart/form-data)
Запит:
POST /api/v1/documents/upload HTTP/1.1
Host: files.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Length: 1024
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="title"
Quarterly Report Q3 2026
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="report.pdf"
Content-Type: application/pdf
[бінарні дані PDF-файлу]
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Відповідь:
HTTP/1.1 201 Created
Location: https://files.example.com/api/v1/documents/456
Content-Type: application/json; charset=utf-8
Content-Length: 187
{"id":456,"title":"Quarterly Report Q3 2026","filename":"report.pdf","size":1048576,"uploadedAt":"2026-08-29T11:25:00Z","url":"/documents/456/download"}
PUT — Повна заміна ресурсу
Призначення: Створення нового ресурсу за вказаною адресою або повна заміна існуючого ресурсу. Клієнт відправляє повне представлення ресурсу, яке замінює поточний стан на сервері.
Характеристики:
- ❌ Небезпечний (змінює стан)
- ✅ Ідемпотентний (повторні запити дають той самий результат)
- ❌ Не кешується
- ✅ Має тіло запиту
- ✅ Має тіло відповіді (опційно)
Семантика: «Замінити ресурс за цією адресою на надані дані» або «Створити ресурс за цією адресою, якщо його ще немає».
Приклад 1: Повна заміна профілю користувача
Запит:
PUT /api/v1/users/42 HTTP/1.1
Host: api.example.com
Content-Type: application/json; charset=utf-8
Authorization: Bearer eyJhbGc...token...
Content-Length: 145
{"email":"john.updated@example.com","firstName":"John","lastName":"Doe","phone":"+380501234567","address":"Kyiv, Ukraine","role":"user"}
Відповідь (ресурс оновлено):
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 11:30:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 198
{"id":42,"email":"john.updated@example.com","firstName":"John","lastName":"Doe","phone":"+380501234567","address":"Kyiv, Ukraine","role":"user","updatedAt":"2026-08-29T11:30:00Z"}
Альтернативна відповідь (без тіла):
HTTP/1.1 204 No Content
Date: Sat, 29 Aug 2026 11:30:00 GMT
Ключовий момент PUT: Клієнт надсилає всі поля ресурсу, навіть ті, що не змінюються. Якщо якесь поле відсутнє у запиті, воно буде видалене або скинуте до значення за замовчуванням на сервері (залежно від реалізації).
Приклад 2: Створення ресурсу за вказаною адресою
Запит (ресурс ще не існує):
PUT /api/v1/articles/my-first-blog-post HTTP/1.1
Host: blog.example.com
Content-Type: application/json; charset=utf-8
Content-Length: 187
{"slug":"my-first-blog-post","title":"My First Blog Post","content":"This is the content of my first article.","tags":["tutorial","beginners"],"published":true}
Відповідь (ресурс створено):
HTTP/1.1 201 Created
Location: https://blog.example.com/api/v1/articles/my-first-blog-post
Content-Type: application/json; charset=utf-8
Content-Length: 234
{"slug":"my-first-blog-post","title":"My First Blog Post","content":"This is the content of my first article.","tags":["tutorial","beginners"],"published":true,"createdAt":"2026-08-29T11:35:00Z"}
Відповідь (якщо ресурс вже існує і був замінений):
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 245
{"slug":"my-first-blog-post","title":"My First Blog Post","content":"This is the content of my first article.","tags":["tutorial","beginners"],"published":true,"updatedAt":"2026-08-29T11:35:00Z"}
Ідемпотентність PUT
Чому PUT є ідемпотентним?
Якщо клієнт надсилає той самий PUT-запит двічі:
PUT /api/v1/users/42 HTTP/1.1
Content-Type: application/json
{"email":"john@example.com","firstName":"John","lastName":"Doe"}
- Після першого запиту: Користувач 42 має дані
{email: "john@example.com", firstName: "John", lastName: "Doe"}. - Після другого запиту: Користувач 42 все ще має дані
{email: "john@example.com", firstName: "John", lastName: "Doe"}.
Стан сервера не змінився між першим та другим запитом — результат ідентичний. Це дозволяє клієнту безпечно повторювати PUT-запити у разі мережевих збоїв.
PATCH — Часткове оновлення ресурсу
Призначення: Часткова модифікація існуючого ресурсу. На відміну від PUT, клієнт надсилає лише ті поля, які потрібно змінити, а решта полів залишаються незмінними.
Характеристики:
- ❌ Небезпечний (змінює стан)
- ⚠️ Умовно ідемпотентний (залежить від реалізації)
- ❌ Не кешується
- ✅ Має тіло запиту
- ✅ Має тіло відповіді
Семантика: «Оновити лише вказані поля ресурсу, залишивши решту без змін».
Приклад 1: Зміна email користувача
Запит:
PATCH /api/v1/users/42 HTTP/1.1
Host: api.example.com
Content-Type: application/json; charset=utf-8
Authorization: Bearer eyJhbGc...token...
Content-Length: 48
{"email":"john.newemail@example.com"}
Відповідь:
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 11:40:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 187
{"id":42,"email":"john.newemail@example.com","firstName":"John","lastName":"Doe","phone":"+380501234567","role":"user","updatedAt":"2026-08-29T11:40:00Z"}
Важливо: Поля firstName, lastName, phone, role не змінилися, оскільки вони не були включені у запит. Оновлено лише email.
Приклад 2: Оновлення декількох полів
Запит:
PATCH /api/v1/products/15 HTTP/1.1
Host: shop.example.com
Content-Type: application/json; charset=utf-8
Content-Length: 68
{"price":19999,"inStock":true,"discountPercent":15}
Відповідь:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 234
{"id":15,"name":"Acer Aspire 5","price":19999,"inStock":true,"discountPercent":15,"category":"laptops","updatedAt":"2026-08-29T11:45:00Z"}
Поля name та category залишилися без змін.
Чому PATCH умовно ідемпотентний?
Ідемпотентність PATCH залежить від типу операції:
Ідемпотентний PATCH (присвоєння значення):
PATCH /api/v1/users/42 HTTP/1.1
Content-Type: application/json
{"status":"active"}
Повторне виконання встановлює status = "active" — результат ідентичний.
Неідемпотентний PATCH (інкремент):
PATCH /api/v1/products/15 HTTP/1.1
Content-Type: application/json
{"views": "+1"}
Якщо це означає «збільшити лічильник переглядів на 1», повторне виконання призведе до подвійного інкременту — результат різний.
DELETE — Видалення ресурсу
Призначення: Видалення ресурсу за вказаною адресою.
Характеристики:
- ❌ Небезпечний (змінює стан)
- ✅ Ідемпотентний (повторне видалення не змінює стан)
- ❌ Не кешується
- ❌ Зазвичай не має тіла запиту (хоча технічно можливе)
- ⚠️ Може мати тіло відповіді (опційно)
Семантика: «Видалити ресурс за цією адресою».
Приклад 1: Видалення користувача
Запит:
DELETE /api/v1/users/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGc...admin-token...
Відповідь (успіх, без тіла):
HTTP/1.1 204 No Content
Date: Sat, 29 Aug 2026 11:50:00 GMT
Альтернативна відповідь (з підтвердженням):
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 11:50:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 89
{"message":"User with ID 42 has been successfully deleted","deletedAt":"2026-08-29T11:50:00Z"}
Відповідь (ресурс не знайдено при повторному видаленні):
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Content-Length: 78
{"error":"Not Found","message":"User with ID 42 does not exist"}
Альтернатива для ідемпотентності: Деякі API повертають 204 No Content навіть якщо ресурс вже був видалений раніше, щоб зберегти ідемпотентність (результат однаковий: ресурс відсутній).
Приклад 2: Видалення товару з кошика
Запит:
DELETE /api/v1/cart/items/87 HTTP/1.1
Host: shop.example.com
Authorization: Bearer eyJhbGc...user-token...
Відповідь:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 156
{"message":"Item removed from cart","cart":{"id":"cart-123","items":[],"totalPrice":0,"updatedAt":"2026-08-29T11:55:00Z"}}
Ідемпотентність DELETE
Чому DELETE є ідемпотентним?
- Перший виклик: Ресурс видаляється, сервер повертає
204 No Contentабо200 OK. - Другий виклик: Ресурс вже відсутній. Сервер може повернути
404 Not Foundабо204 No Content.
Стан сервера після другого виклику ідентичний стану після першого: ресурс відсутній. Навіть якщо коди статусу різні (204 vs 404), ефект на стан ресурсу однаковий — ресурс не існує.
deleted=true або deletedAt=timestamp). Це дозволяє відновити ресурс пізніше та зберегти історію. Для клієнта поведінка залишається незмінною — ресурс більше недоступний через GET-запити.OPTIONS — Запит можливостей сервера
Призначення: Запит підтримуваних методів та можливостей для певного ресурсу або всього сервера. Найчастіше використовується браузерами для preflight-запитів CORS.
Характеристики:
- ✅ Безпечний
- ✅ Ідемпотентний
- ❌ Не кешується
- ❌ Не має тіла запиту
- ✅ Має тіло відповіді (опційно)
Семантика: «Які методи та заголовки підтримуються для цього ресурсу?»
Приклад 1: Запит можливостей для ресурсу
Запит:
OPTIONS /api/v1/users/42 HTTP/1.1
Host: api.example.com
Відповідь:
HTTP/1.1 204 No Content
Date: Sat, 29 Aug 2026 12:00:00 GMT
Allow: GET, PUT, PATCH, DELETE, OPTIONS
Content-Length: 0
Заголовок Allow перераховує підтримувані методи для цього ресурсу.
Приклад 2: Preflight-запит CORS
Запит (браузер автоматично перед POST):
OPTIONS /api/v1/users HTTP/1.1
Host: api.example.com
Origin: https://frontend.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
Відповідь (сервер дозволяє запит):
HTTP/1.1 204 No Content
Date: Sat, 29 Aug 2026 12:05:00 GMT
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Content-Length: 0
Після успішного preflight браузер автоматично відправляє реальний POST-запит.
Приклад 3: Запит можливостей всього сервера
Запит:
OPTIONS * HTTP/1.1
Host: api.example.com
Відповідь:
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 12:10:00 GMT
Allow: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: text/plain
Content-Length: 0
TRACE — Діагностичний луна-запит
Призначення: Виконує луна-тест (loopback test) запиту — сервер повертає отриманий запит назад клієнту у тілі відповіді. Використовується для діагностики мережевих проблем та перевірки модифікацій запиту проміжними проксі.
Характеристики:
- ✅ Безпечний
- ✅ Ідемпотентний
- ❌ Не кешується
- ❌ Не має тіла запиту
- ✅ Має тіло відповіді (відображення запиту)
Семантика: «Поверни мені назад той запит, який ти отримав».
Приклад:
Запит:
TRACE /api/v1/test HTTP/1.1
Host: api.example.com
User-Agent: curl/7.88.0
Accept: */*
Відповідь:
HTTP/1.1 200 OK
Date: Sat, 29 Aug 2026 12:15:00 GMT
Content-Type: message/http
Content-Length: 98
TRACE /api/v1/test HTTP/1.1
Host: api.example.com
User-Agent: curl/7.88.0
Accept: */*
Authorization, Cookie) через відображення запиту. Більшість сучасних серверів вимикають TRACE за замовчуванням або обмежують його використання. У продакшені рекомендується заборонити TRACE через конфігурацію веб-сервера.CONNECT — Встановлення тунелю
Призначення: Встановлення мережевого тунелю до цільового сервера через проксі. Використовується переважно для HTTPS-з'єднань через HTTP-проксі (HTTPS tunneling).
Характеристики:
- ❌ Небезпечний
- ❌ Неідемпотентний
- ❌ Не кешується
- ❌ Не має тіла запиту
- ⚠️ Після успішного встановлення тунелю дані передаються у бінарному форматі
Семантика: «Встанови TCP-тунель до вказаного хоста через проксі».
Приклад:
Запит:
CONNECT api.example.com:443 HTTP/1.1
Host: api.example.com:443
Відповідь (успіх):
HTTP/1.1 200 Connection Established
Після цього клієнт та цільовий сервер спілкуються напряму через проксі як через прозорий тунель. Весь трафік (включно з TLS handshake) проходить через проксі без розшифрування.
Ключові відмінності: PUT vs PATCH vs POST
Ці три методи часто плутають, оскільки всі вони можуть змінювати дані на сервері. Розглянемо детальне порівняння:
| Характеристика | POST | PUT | PATCH |
|---|---|---|---|
| Призначення | Створення нового ресурсу або виконання операції | Повна заміна ресурсу або створення за вказаною адресою | Часткове оновлення ресурсу |
| Ідемпотентність | ❌ Неідемпотентний | ✅ Ідемпотентний | ⚠️ Умовно ідемпотентний |
| URI ресурсу | Зазвичай колекція (/users) | Конкретний ресурс (/users/42) | Конкретний ресурс (/users/42) |
| Тіло запиту | Дані нового ресурсу | Повне представлення ресурсу | Лише поля для оновлення |
| Відсутні поля | — | Видаляються або скидаються | Залишаються без змін |
| Повторне виконання | Створює дублікат | Результат ідентичний | Залежить від операції |
| Код успіху | 201 Created або 200 OK | 200 OK або 204 No Content | 200 OK або 204 No Content |
| Типовий use case | Створення користувача, відправка форми | Заміна всіх даних профілю | Зміна email або телефону |
Практичний приклад порівняння
Початковий стан ресурсу:
{
"id": 42,
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
"phone": "+380501111111"
}
POST (створення нового ресурсу):
POST /api/v1/users HTTP/1.1
Content-Type: application/json
{"email":"jane@example.com","firstName":"Jane","lastName":"Smith"}
Результат: Створюється новий користувач з id: 43.
PUT (повна заміна):
PUT /api/v1/users/42 HTTP/1.1
Content-Type: application/json
{"email":"john.new@example.com","firstName":"John","lastName":"Doe"}
Результат (користувач 42):
{
"id": 42,
"email": "john.new@example.com",
"firstName": "John",
"lastName": "Doe",
"phone": null // ⚠️ Видалено, бо не було у запиті
}
PATCH (часткове оновлення):
PATCH /api/v1/users/42 HTTP/1.1
Content-Type: application/json
{"email":"john.updated@example.com"}
Результат (користувач 42):
{
"id": 42,
"email": "john.updated@example.com", // ✅ Оновлено
"firstName": "John", // ✅ Залишилося без змін
"lastName": "Doe", // ✅ Залишилося без змін
"phone": "+380501111111" // ✅ Залишилося без змін
}
Візуалізація семантики методів
Підсумок розділу
📚 Основні висновки
- HTTP-методи визначають тип операції над ресурсом: читання (GET, HEAD), створення (POST), оновлення (PUT, PATCH), видалення (DELETE).
- Безпечні методи (GET, HEAD, OPTIONS) не змінюють стан сервера та можуть кешуватися.
- Ідемпотентні методи (GET, PUT, DELETE) можна безпечно повторювати при мережевих збоях — результат буде ідентичним.
- POST створює новий ресурс (неідемпотентний), PUT замінює повністю (ідемпотентний), PATCH оновлює частково (умовно ідемпотентний).
- Правильний вибір методу критичний для RESTful API та впливає на безпеку, продуктивність та надійність системи.
🎓 Навички, набуті у розділі
- Розуміння семантики кожного HTTP-методу та контексту його використання.
- Вміння обирати правильний метод для конкретного сценарію (створення, оновлення, видалення).
- Розуміння концепцій безпечності, ідемпотентності та кешованості методів.
- Знання відмінностей між PUT та PATCH, POST та PUT у контексті REST API.
Контрольні запитання для самоперевірки
PUT є ідемпотентним, оскільки він замінює ресурс повністю на надані дані. Якщо клієнт надсилає той самий PUT-запит двічі, результат буде ідентичним — ресурс матиме ті самі дані після другого виклику, що й після першого.
Приклад:
PUT /api/v1/users/42 HTTP/1.1
Content-Type: application/json
{"email":"john@example.com","name":"John"}
- 1-й виклик: Користувач 42 має
{email: "john@example.com", name: "John"}. - 2-й виклик: Користувач 42 все ще має
{email: "john@example.com", name: "John"}.
Стан незмінний, тому PUT ідемпотентний.
POST є неідемпотентним, оскільки він зазвичай створює новий ресурс при кожному виклику. Повторне виконання POST призводить до створення дублікатів.
Приклад:
POST /api/v1/users HTTP/1.1
Content-Type: application/json
{"email":"jane@example.com","name":"Jane"}
- 1-й виклик: Створюється користувач з ID 100.
- 2-й виклик: Створюється новий користувач з ID 101 (дублікат).
Стан змінюється при кожному виклику, тому POST неідемпотентний.
Використовуйте PUT, коли:
- Клієнт має повне представлення ресурсу та хоче його замінити цілком.
- Ви бажаєте забезпечити ідемпотентність операції оновлення.
- API дизайн передбачає, що відсутні поля повинні бути видалені або скинуті.
Приклад use case: Оновлення профілю користувача через форму, де всі поля обов'язкові та відомі клієнту.
Використовуйте PATCH, коли:
- Клієнт хоче оновити лише окремі поля ресурсу, залишивши решту без змін.
- Надсилання повного представлення ресурсу є неефективним (великі об'єкти, багато полів).
- Оновлення виконується з різних місць інтерфейсу, і кожне місце відповідає за окремі поля.
Приклад use case: Зміна лише email через окрему форму налаштувань, не зачіпаючи ім'я, адресу та інші поля.
Золоте правило: Якщо клієнт надсилає всі поля — використовуйте PUT. Якщо лише частину полів — використовуйте PATCH.
Технічно — так, специфікація HTTP не забороняє тіло у GET-запитах. Проте практично — ні, з кількох причин:
- Семантика GET: Метод GET призначений для безпечного отримання ресурсу. Наявність тіла суперечить цій семантиці, оскільки тіло асоціюється з модифікацією або відправкою даних.
- Кешування: Кеші (браузери, CDN, проксі) використовують URL (включно з query string) як ключ для кешування GET-запитів. Якщо параметри знаходяться у тілі, кеші не зможуть правильно ідентифікувати унікальність запиту.
- Підтримка серверів та проксі: Багато HTTP-серверів, проксі та бібліотек ігнорують або відхиляють тіло GET-запиту. Наприклад, nginx за замовчуванням не читає тіло GET.
- Інструменти розробки: curl, Postman, браузерні DevTools можуть вести себе непередбачувано при спробі надіслати GET з тілом.
Рекомендація: Завжди передавайте параметри GET через query string у URL. Якщо параметрів занадто багато або вони конфіденційні, використовуйте POST замість GET.
Метод HEAD виконує ту саму функцію, що й GET, але без передачі тіла відповіді. Це дозволяє клієнту отримати метадані про ресурс (розмір, тип, час модифікації, наявність) без завантаження самого вмісту.
Практичні сценарії використання HEAD:
- Перевірка існування ресурсу:
HEAD /api/v1/users/42 HTTP/1.1
Відповідь200 OKозначає, що користувач існує.404 Not Found— не існує. Тіло не завантажується. - Перевірка розміру файлу перед завантаженням:
HEAD /downloads/large-video.mp4 HTTP/1.1
Клієнт отримуєContent-Length: 2147483648(2 ГБ) та може попередити користувача про великий розмір. - Валідація кешу без завантаження даних:
HEAD /api/v1/products/15 HTTP/1.1 If-None-Match: "product-v2-hash"
Відповідь304 Not Modifiedозначає, що закешована версія актуальна, без завантаження JSON. - Моніторинг доступності API (health checks):
HEAD /health HTTP/1.1
Швидка перевірка, що сервер відповідає, без генерації повного тіла відповіді.
Переваги HEAD: Економія пропускної здатності, прискорення перевірок, зменшення навантаження на сервер (не потрібно генерувати тіло).
У наступному розділі ми детально розглянемо коди статусу HTTP — класифікацію груп 1xx–5xx, семантику найпоширеніших кодів та правила їхнього використання у REST API.