ШПАРГАЛКА

HTTP и REST API

HTTP и REST API: методы (GET/POST/PUT/PATCH/DELETE), статус-коды, заголовки, тело запроса, REST, идемпотентность, аутентификация. Примеры curl и fetch.

HTTP (HyperText Transfer Protocol) — это текстовый протокол прикладного уровня, на котором держится почти весь обмен данными в вебе. Клиент (браузер, мобильное приложение, curl) шлёт запрос, сервер возвращает ответ. Протокол работает по схеме «запрос-ответ» и без состояния (stateless): каждый запрос самодостаточен, сервер не обязан помнить предыдущие. Эта шпаргалка собирает методы, статус-коды, заголовки, тело, принципы REST и аутентификацию в одном месте — с примерами curl и fetch.

Что такое HTTP: запрос и ответ

Обмен идёт парами: один запрос — один ответ. Запрос задаёт метод (что сделать), URL (с чем) и набор заголовков (метаданные), иногда — тело (данные). Ответ возвращает статус-код (как прошло), свои заголовки и обычно тело (результат).

Stateless означает, что HTTP сам по себе не хранит сессию. Чтобы «узнавать» клиента между запросами, используют куки (Cookie) или токены (Authorization). Современные версии: HTTP/1.1 (текстовый), HTTP/2 (бинарный, мультиплексирование), HTTP/3 (поверх QUIC/UDP) — но семантика методов и кодов одинакова.

# Самый простой запрос: получить страницу и показать заголовки ответа
curl -i https://api.example.com/ping

HTTP-методы (глаголы)

Метод задаёт намерение. Важны два свойства: безопасный (safe) — не меняет данные на сервере; идемпотентный — повторный одинаковый запрос даёт тот же результат, что и один.

МетодНазначениеТело запросаБезопасныйИдемпотентный
GETПолучить ресурс или списокнетдада
POSTСоздать ресурс, отправить данные, запустить действиеданетнет
PUTПолностью заменить ресурс (или создать по известному URL)данетда
PATCHЧастично обновить ресурсданетнет*
DELETEУдалить ресурсобычно нетнетда
HEADКак GET, но только заголовки, без тела (проверка наличия/размера)нетдада
OPTIONSУзнать допустимые методы и CORS-политику (preflight)нетдада

* PATCH идемпотентен только если операция сформулирована так (например, «установить поле в значение»). Операции вида «увеличить на 1» не идемпотентны.

# GET — получить пользователя
curl https://api.example.com/users/42

# POST — создать пользователя
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Аня", "email": "[email protected]"}'

# PUT — полностью заменить
curl -X PUT https://api.example.com/users/42 \
  -H "Content-Type: application/json" \
  -d '{"name": "Аня", "email": "[email protected]"}'

# PATCH — обновить одно поле
curl -X PATCH https://api.example.com/users/42 \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]"}'

# DELETE — удалить
curl -X DELETE https://api.example.com/users/42

# HEAD — только заголовки (узнать размер файла)
curl -I https://api.example.com/files/report.pdf

# OPTIONS — какие методы разрешены
curl -X OPTIONS https://api.example.com/users -i

Структура HTTP-запроса

Запрос состоит из стартовой строки (метод + путь + версия), заголовков (по одному на строку, формат Имя: значение), пустой строки и необязательного тела.

POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJhbGci...
Content-Length: 48

{"name": "Аня", "email": "[email protected]"}

Стартовая строка: POST /users HTTP/1.1. Host обязателен в HTTP/1.1. Пустая строка отделяет заголовки от тела. Тело идёт только у методов, которые его поддерживают (POST/PUT/PATCH).

Структура HTTP-ответа

Ответ состоит из строки статуса (версия + код + текст), заголовков, пустой строки и тела.

HTTP/1.1 201 Created
Content-Type: application/json
Location: /users/42
Content-Length: 60
Date: Mon, 16 Jun 2026 10:00:00 GMT

{"id": 42, "name": "Аня", "email": "[email protected]"}

201 Created — код результата. Location указывает на созданный ресурс. Content-Type описывает формат тела. Дальше — пустая строка и сам JSON.

Статус-коды

Код состоит из трёх цифр; первая задаёт класс. Запоминать стоит классы и ключевые коды.

КлассСмысл
1xxИнформационные — запрос принят, обработка продолжается (редко видны в коде)
2xxУспех — запрос принят и обработан
3xxПеренаправление — нужно доп. действие (часто переход по другому URL)
4xxОшибка клиента — проблема в самом запросе
5xxОшибка сервера — запрос корректен, но сервер не справился

Ключевые коды

КодНазваниеКогда
200OKУспех, тело содержит результат (типично для GET)
201CreatedРесурс создан (типично для POST), часто с заголовком Location
204No ContentУспех, но тела нет (типично для DELETE, PUT без ответа)
301Moved PermanentlyРесурс навсегда переехал на новый URL (кешируется)
302FoundВременное перенаправление на другой URL
304Not ModifiedРесурс не изменился, используйте кеш (ответ на условный запрос)
400Bad RequestНекорректный запрос (битый JSON, не тот формат)
401UnauthorizedНе аутентифицирован — нет/невалиден токен или логин
403ForbiddenАутентифицирован, но нет прав на действие
404Not FoundРесурс не найден по этому URL
409ConflictКонфликт состояния (например, дубликат, гонка версий)
422Unprocessable EntityТело валидно по формату, но не проходит валидацию данных
429Too Many RequestsПревышен лимит запросов (rate limit), см. Retry-After
500Internal Server ErrorНеобработанная ошибка на сервере
502Bad GatewayПрокси/шлюз получил неверный ответ от вышестоящего сервера
503Service UnavailableСервер временно недоступен (перегрузка, обслуживание)

Разница 401 vs 403: 401 — «я не знаю, кто ты» (нет валидной аутентификации), 403 — «я знаю, кто ты, но тебе нельзя». 400 vs 422: 400 — запрос вообще не разобрать (битый синтаксис), 422 — разобрали, но данные не прошли бизнес-валидацию.

Заголовки (Headers)

Заголовки несут метаданные о запросе или ответе: формат тела, авторизацию, кеширование, куки. Имена регистронезависимы.

ЗаголовокГдеНазначение
Content-Typeзапрос/ответФормат тела: application/json, text/html, application/x-www-form-urlencoded, multipart/form-data
AcceptзапросКакие форматы ответа клиент готов принять
AuthorizationзапросУчётные данные: Bearer <token>, Basic <base64>
Cache-Controlзапрос/ответПолитика кеша: no-cache, max-age=3600, no-store
CookieзапросКуки, отправляемые клиентом серверу
Set-CookieответСервер просит клиента сохранить куку
Content-Lengthзапрос/ответРазмер тела в байтах
LocationответURL созданного ресурса (201) или цель редиректа (3xx)
User-AgentзапросИдентификатор клиента (браузер/библиотека)
ETagответВерсия ресурса для условных запросов и кеша
Retry-AfterответЧерез сколько повторить (при 429/503)

CORS-заголовки

CORS (Cross-Origin Resource Sharing) разрешает браузеру обращаться к API на другом домене. Сервер отвечает специальными заголовками; для «сложных» запросов браузер сначала шлёт preflight (OPTIONS).

ЗаголовокНазначение
Access-Control-Allow-OriginКакие источники могут обращаться (* или конкретный домен)
Access-Control-Allow-MethodsРазрешённые методы (GET, POST, PUT, DELETE)
Access-Control-Allow-HeadersКакие заголовки клиент может слать (например, Authorization, Content-Type)
Access-Control-Allow-CredentialsРазрешить отправку кук/учётных данных (true)
Access-Control-Max-AgeСколько секунд кешировать результат preflight
# Передать несколько заголовков
curl https://api.example.com/users/42 \
  -H "Accept: application/json" \
  -H "Authorization: Bearer eyJhbGci..." \
  -H "Cache-Control: no-cache"

# Посмотреть preflight-ответ CORS
curl -X OPTIONS https://api.example.com/users \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" -i

Тело запроса: JSON и формы

Тело несут методы POST/PUT/PATCH. Формат указывается в Content-Type.

JSON (application/json)

Самый частый формат для REST API. Сервер парсит тело как JSON.

curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Аня", "age": 25, "roles": ["admin", "editor"]}'

URL-кодированная форма (application/x-www-form-urlencoded)

Классические HTML-формы. Пары ключ=значение, разделённые &.

curl -X POST https://api.example.com/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=anya&password=secret"

Файлы (multipart/form-data)

Для загрузки файлов и смешанных данных.

curl -X POST https://api.example.com/upload \
  -F "file=@/path/to/photo.jpg" \
  -F "title=Моё фото"

Принципы REST

REST (Representational State Transfer) — архитектурный стиль для API поверх HTTP. Ключевые идеи:

  • Ресурсы и URL. Всё — это ресурс с адресом. Существительные во множественном числе: /users, /users/42, /users/42/orders. Не глаголы (/getUser — антипаттерн).
  • Методы как действия. Не кодируйте действие в URL — используйте HTTP-метод: GET читать, POST создавать, PUT/PATCH менять, DELETE удалять.
  • Stateless. Сервер не хранит сессию между запросами; всё нужное (токен, параметры) приходит в каждом запросе.
  • Представления. Один ресурс может отдаваться в разных форматах (JSON, XML) — клиент выбирает через Accept.
  • Коды статусов по смыслу. 200/201/204 для успеха, 4xx для ошибок клиента, 5xx для ошибок сервера — а не «всегда 200 с полем error».
  • Единообразие. Предсказуемые URL, пагинация (?page=2&limit=20), фильтрация (?status=active), версионирование (/v1/users).
ДействиеМетод + URLТипичный код
Список пользователейGET /users200
Один пользовательGET /users/42200 / 404
СоздатьPOST /users201
Заменить целикомPUT /users/42200 / 204
Частично обновитьPATCH /users/42200
УдалитьDELETE /users/42204

Идемпотентность методов

Идемпотентный метод можно повторять сколько угодно раз — состояние сервера будет таким же, как после одного запроса. Это важно для надёжных повторов при обрыве сети.

МетодИдемпотентныйПочему
GETдаТолько читает, ничего не меняет
HEADдаКак GET, без тела
OPTIONSдаТолько описывает возможности
PUTдаЗаменяет ресурс целиком — результат один и тот же
DELETEдаПовторное удаление того же ресурса не меняет итог (его уже нет)
POSTнетКаждый вызов обычно создаёт новый ресурс
PATCHнет*Зависит от операции (замена — да, инкремент — нет)

Практика: повторять автоматически безопасно только идемпотентные запросы. Для POST, чтобы избежать дублей, используют ключ идемпотентности (Idempotency-Key).

Аутентификация

HTTP сам по себе не помнит клиента, поэтому учётные данные шлют в каждом запросе — обычно в заголовке Authorization.

Basic Auth

Логин и пароль в виде base64(login:password). Просто, но передаёт пароль почти в открытом виде — только поверх HTTPS.

# curl сам закодирует логин:пароль в base64
curl -u anya:secret https://api.example.com/private
# Эквивалент вручную:
curl https://api.example.com/private \
  -H "Authorization: Basic YW55YTpzZWNyZXQ="

Bearer / JWT

Токен (часто JWT) передаётся как Bearer <token>. JWT — это самодостаточный токен из трёх частей (header.payload.signature), подписанный сервером.

curl https://api.example.com/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

API-ключ

Простой статичный ключ — в заголовке или query-параметре. Удобен для сервер-сервер интеграций.

# В отдельном заголовке
curl https://api.example.com/data -H "X-API-Key: ab12cd34ef56"
# Или как параметр URL (менее безопасно — попадает в логи)
curl "https://api.example.com/data?api_key=ab12cd34ef56"
СпособЗаголовокПлюсы / минусы
BasicAuthorization: Basic ...Простой; пароль в каждом запросе
Bearer/JWTAuthorization: Bearer ...Без пароля, с истечением срока; нужно обновлять токен
API-keyX-API-Key: ...Удобно для интеграций; ключ долгоживущий, легко утекает

Примеры: curl и fetch

GET с авторизацией

curl https://api.example.com/users/42 \
  -H "Authorization: Bearer TOKEN"
const res = await fetch("https://api.example.com/users/42", {
  headers: { "Authorization": "Bearer TOKEN" }
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const user = await res.json();

POST с JSON

curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer TOKEN" \
  -d '{"name": "Аня", "email": "[email protected]"}'
const res = await fetch("https://api.example.com/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer TOKEN"
  },
  body: JSON.stringify({ name: "Аня", email: "[email protected]" })
});
const created = await res.json(); // ожидаем статус 201

PATCH и обработка ошибок

const res = await fetch("https://api.example.com/users/42", {
  method: "PATCH",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ email: "[email protected]" })
});

if (res.status === 422) {
  const errors = await res.json(); // данные не прошли валидацию
  console.error("Ошибки валидации:", errors);
} else if (res.status === 401) {
  console.error("Нужна авторизация");
} else if (res.ok) {
  console.log("Обновлено:", await res.json());
}

DELETE

curl -X DELETE https://api.example.com/users/42 \
  -H "Authorization: Bearer TOKEN" -i   # ждём 204 No Content
const res = await fetch("https://api.example.com/users/42", {
  method: "DELETE",
  headers: { "Authorization": "Bearer TOKEN" }
});
console.log(res.status === 204 ? "Удалён" : "Не удалось");
Поддержать проект