Протокол HTTP та захищена передача даних HTTPS

Методи 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-сервера.

Хоча POST технічно може бути кешованим згідно зі специфікацією, на практиці браузери та проксі ніколи не кешують POST-запити, оскільки вони зазвичай змінюють стан сервера. Кешуються лише GET та HEAD запити.

Порівняльна таблиця HTTP-методів

Для систематизації знань наведемо порівняльну таблицю характеристик всіх стандартних методів HTTP/1.1:

МетодБезпечнийІдемпотентнийКешованийМає тіло запитуМає тіло відповідіТипове використання
GET❌ (рідко)Отримання ресурсу
QUERY⚠️ (складно)Складний пошук з фільтрами у тілі
HEADОтримання метаданих без тіла
POST⚠️ (теоретично)Створення ресурсу, відправка даних
PUTПовна заміна ресурсу
PATCH⚠️ (залежить)Часткове оновлення ресурсу
DELETE❌ (опційно)✅ (опційно)Видалення ресурсу
OPTIONSЗапит можливостей сервера
TRACEДіагностика (луна-запит)
CONNECTВстановлення тунелю (HTTPS через проксі)
Для розробників REST API найважливішими методами є GET (читання), POST (створення), PUT (повна заміна), PATCH (часткове оновлення) та DELETE (видалення). Метод QUERY є корисним доповненням для складних пошукових сценаріїв, хоча він все ще є експериментальним. Інші методи використовуються рідше та у спеціалізованих сценаріях.

Детальний розгляд 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}

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

Проте цей підхід має обмеження:

  1. Довжина URL обмежена: Більшість серверів обмежують довжину URL (зазвичай 2048–8192 символів). Складні фільтри з багатьма параметрами можуть перевищити цей ліміт.
  2. Складність структурованих даних: Передача вкладених об'єктів, масивів, логічних виразів через query string є незручною та нечитабельною.
  3. Конфіденційність: Query string логується у всіх проміжних системах (проксі, балансувальники, логи сервера), що може бути небезпечним для чутливих критеріїв пошуку.
  4. Кешування: 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

  1. Необмежена складність: Можна передавати глибоко вкладені фільтри, логічні оператори (AND, OR, NOT), діапазони дат, масиви значень.
  2. Читабельність: JSON у тілі є структурованішим та зрозумілішим, ніж довгий query string.
  3. Безпека: Критерії пошуку не логуються у URL, що зменшує ризик витоку чутливої інформації.
  4. Гнучкість типізації: JSON підтримує булеві значення, числа, null, вкладені об'єкти без необхідності кодування.
  5. Відсутність обмежень довжини URL: Немає ризику перевищити ліміт довжини URL.

Обмеження та підтримка QUERY

Статус специфікації: Метод QUERY все ще є експериментальним та не входить до офіційного стандарту HTTP/1.1 (RFC 7231). Він визначений у чернетці draft-ietf-httpbis-safe-method-w-body, яка знаходиться у статусі обговорення у IETF.Підтримка:
  • Більшість стандартних веб-серверів (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

{...складні критерії пошуку...}
Проте це порушує семантику HTTP, оскільки POST є небезпечним методом. QUERY є кращим рішенням, коли підтримка доступна.
Рекомендація для backend-розробників:
Якщо ваше API вимагає складних пошукових запитів:
  1. Ідеально: Впровадити підтримку методу QUERY, якщо ваш фреймворк це дозволяє.
  2. Компроміс: Використовувати POST /resource/search з чітким документуванням, що це безпечна операція пошуку.
  3. Традиційно: Використовувати GET з обмеженими фільтрами через query string для простих запитів.
Для максимальної сумісності можна підтримувати обидва підходи — GET для простих фільтрів та QUERY/POST для складних.

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"}
Важливо: POST є неідемпотентним — повторне виконання POST-запиту створює новий ресурс кожного разу. Якщо клієнт не отримав відповідь через мережевий збій і повторює запит, може виникнути дублікат. Для уникнення цієї проблеми використовуйте ідемпотентні токени (idempotency keys) — унікальні ідентифікатори запиту, які сервер перевіряє перед створенням ресурсу.

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», повторне виконання призведе до подвійного інкременту — результат різний.

Для забезпечення ідемпотентності PATCH використовуйте абсолютні значення замість відносних операцій (інкремент, декремент, додавання до масиву). Якщо потрібні складні операції модифікації, розгляньте використання специфікації JSON Patch (RFC 6902) або JSON Merge Patch (RFC 7396).

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), ефект на стан ресурсу однаковий — ресурс не існує.

Soft Delete vs Hard Delete: У багатьох системах DELETE не видаляє ресурс фізично з бази даних, а лише позначає його як видалений (soft delete — встановлення прапорця 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: */*
Безпека TRACE: Метод TRACE може бути використаний для атак Cross-Site Tracing (XST), де зловмисник може витягнути конфіденційні заголовки (наприклад, 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) проходить через проксі без розшифрування.

Метод CONNECT рідко використовується безпосередньо розробниками застосунків — він є частиною внутрішньої реалізації HTTP-клієнтів при роботі через проксі-сервери.

Ключові відмінності: PUT vs PATCH vs POST

Ці три методи часто плутають, оскільки всі вони можуть змінювати дані на сервері. Розглянемо детальне порівняння:

ХарактеристикаPOSTPUTPATCH
ПризначенняСтворення нового ресурсу або виконання операціїПовна заміна ресурсу або створення за вказаною адресоюЧасткове оновлення ресурсу
Ідемпотентність❌ Неідемпотентний✅ Ідемпотентний⚠️ Умовно ідемпотентний
URI ресурсуЗазвичай колекція (/users)Конкретний ресурс (/users/42)Конкретний ресурс (/users/42)
Тіло запитуДані нового ресурсуПовне представлення ресурсуЛише поля для оновлення
Відсутні поляВидаляються або скидаютьсяЗалишаються без змін
Повторне виконанняСтворює дублікатРезультат ідентичнийЗалежить від операції
Код успіху201 Created або 200 OK200 OK або 204 No Content200 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"              // ✅ Залишилося без змін
}

Візуалізація семантики методів

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #FFFFFF

package "REST API Resource: /api/v1/users" #E0F2FE {
    
    rectangle "Collection\n/api/v1/users" #DBEAFE {
        rectangle "GET\nОтримати список\nусіх користувачів" as GET_COL #DCFCE7
        rectangle "POST\nСтворити нового\nкористувача" as POST_COL #FEF3C7
    }
    
    rectangle "Single Resource\n/api/v1/users/{id}" #BAE6FD {
        rectangle "GET\nОтримати\nконкретного\nкористувача" as GET_ONE #DCFCE7
        rectangle "PUT\nПовна заміна\nусіх даних\nкористувача" as PUT_ONE #FEF9E7
        rectangle "PATCH\nЧасткове\nоновлення\nполів" as PATCH_ONE #FEF9E7
        rectangle "DELETE\nВидалити\nкористувача" as DELETE_ONE #FECACA
        rectangle "HEAD\nОтримати\nметадані\nбез тіла" as HEAD_ONE #E0E7FF
        rectangle "OPTIONS\nЗапит\nпідтримуваних\nметодів" as OPT_ONE #F0FDF4
    }
}

note right of GET_COL
  Безпечний, ідемпотентний, кешований.
  Не змінює стан сервера.
end note

note right of POST_COL
  Небезпечний, **неідемпотентний**.
  Кожен виклик створює новий ресурс.
end note

note right of PUT_ONE
  Небезпечний, **ідемпотентний**.
  Повторний виклик дає той самий результат.
  Замінює **всі** поля ресурсу.
end note

note right of PATCH_ONE
  Небезпечний, умовно ідемпотентний.
  Оновлює **лише** вказані поля.
end note

note right of DELETE_ONE
  Небезпечний, **ідемпотентний**.
  Повторне видалення не змінює стан
  (ресурс відсутній).
end note

@enduml

Підсумок розділу

📚 Основні висновки

  • 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.

Контрольні запитання для самоперевірки


У наступному розділі ми детально розглянемо коди статусу HTTP — класифікацію груп 1xx–5xx, семантику найпоширеніших кодів та правила їхнього використання у REST API.

Copyright © 2026