Модуль 1 · API как контракт

Справочник «Стандартизация HTTP API» · модуль 1 · API как контракт

Глава 1.2

1.2

Один обмен:
запрос и ответ

Все правила темы описывают одно и то же событие — обмен двумя сообщениями. В этой главе оба сообщения разобраны по частям: из чего собран запрос, из чего ответ, что означает каждый заголовок и как посмотреть обмен целиком на своём компьютере.

Категория
Основы HTTP
Уровень
Начальный
Связанные главы
1.3 Ресурсы и адреса · 2.2 Методы · 2.3 Коды ответов · 4.3 Заголовки контракта
Зачем это знать
Чтобы читать чужой запрос и составлять свой, не угадывая смысл частей

§1Обмен — это два сообщения

HTTP устроен как переписка, а не как разговор по телефону. Клиент отправляет одно сообщение — запрос. Сервер отвечает одним сообщением — ответом. После этого обмен закончен: сервер не помнит клиента, а клиент не ждёт продолжения. Следующий запрос того же клиента начинается для сервера с чистого листа.

Из этого свойства следует практическое правило (подробно — глава 2.1): всё необходимое для обработки должно приходить в самом запросе. Нельзя построить контракт «сначала вызови A, сервер запомнит, потом вызови B»: между A и B клиента может обслужить другой экземпляр сервиса, который ничего не запоминал.

ОпределениеHTTP-сообщение

Текстовая по форме единица обмена: стартовая строка, набор заголовков, пустая строка и необязательное тело. У запроса стартовая строка содержит метод и адрес, у ответа — код состояния.

Обе стороны собирают сообщение по одинаковым правилам, поэтому запрос и ответ читаются похоже. Разница — в первой строке и в назначении заголовков: в запросе они описывают отправляемое, в ответе — возвращаемое.

§2Первая строка запроса: метод и адрес

Первая строка запроса отвечает на два вопроса: что сделать и с чем. Первое слово — метод, второе — адрес ресурса.

первая строка запроса
POST /api/v1/tickets HTTP/1.1

Метод POST означает «создай», адрес /api/v1/tickets — «в коллекции обращений». Ни одно из этих слов не повторяет другое: адрес называет ресурс, метод называет действие. Поэтому адреса вида /createTicket считаются ошибкой — действие в них сказано дважды, причём вторая формулировка не проверяется ничем.

В учебном сервисе используются пять сочетаний метода и адреса. Они и составляют контракт Service Desk API:

ЗапросЧто делаетУспешный код
GET /api/v1/ticketsстраница обращений с фильтром и пагинацией200
POST /api/v1/ticketsсоздать обращение201 + Location
GET /api/v1/tickets/{ticket_id}одно обращение200 + ETag
PATCH /api/v1/tickets/{ticket_id}изменить переданные поля200
DELETE /api/v1/tickets/{ticket_id}удалить обращение204

Часть адреса /api/v1 — это мажорная версия контракта: когда понадобится несовместимое изменение, рядом появится /api/v2, и старые клиенты продолжат работать со своей версией. Подробно — в модуле 5.

§3Заголовки запроса

Заголовки — это сведения о сообщении: кто отправил, в каком формате тело, что клиент готов принять, при каких условиях выполнять операцию. Каждый заголовок решает одну задачу, и путать их нельзя.

AuthorizationКто отправил запрос. В учебном проекте — Bearer demo-token. Без него операции записи отвечают 401.кто это
Content-TypeВ каком формате тело запроса. Для наших операций — application/json. Без него сервер видит байты и не знает, как их разбирать.как читать тело
AcceptЧто клиент готов принять в ответ. Позволяет одному ресурсу иметь несколько представлений.что вернуть
If-MatchУсловие: выполнить изменение, только если ресурс не менялся с указанной версии. Иначе 412.условие
Idempotency-KeyКлюч повтора: если ответ потерялся и клиент повторил запрос, второе обращение не создастся.защита от дублей
X-Request-IdИдентификатор запроса для логов. Клиент может прислать свой, сервер вернёт его в ответе.поиск в логах

Первые три заголовка стандартны и встречаются в любом HTTP API. Три последних — часть контракта конкретного сервиса: их нужно объявить в описании, иначе клиент о них не узнает. Разбор каждого — в главе 4.3.

Регистр имён

Имена заголовков не чувствительны к регистру: Content-Type и content-type — один и тот же заголовок. В примерах справочника запросы записаны с заглавных букв, а ответы — так, как их печатает сервер, строчными.

§4Тело запроса

Тело содержит данные, которые клиент передаёт серверу. Оно есть не у каждого запроса: GET и DELETE обходятся методом и адресом, а POST и PATCH без тела бессмысленны.

POST /api/v1/tickets · тело запроса
{
  "title": "Не работает VPN",
  "description": "Ошибка подключения после обновления клиента"
}

В теле нет ни идентификатора, ни статуса, ни времени создания — их заполняет сервер. Это часть контракта: схема TicketCreate запрещает лишние поля, и запрос с id получит 422 ещё до того, как дойдёт до кода обработчика.

ответ на попытку прислать свой id
HTTP/1.1 422
content-type: application/problem+json

{
  "type": "…/problems/http-422",
  "title": "Validation error",
  "status": 422,
  "detail": "Request does not match the API contract"
}

При изменении тело содержит только те поля, которые нужно изменить. Остальные остаются прежними: метод PATCH выполняет частичное изменение.

PATCH /api/v1/tickets/{ticket_id} · тело запроса
{
  "status": "closed"
}

§5Ответ: код, заголовки, тело

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

ответ на POST /api/v1/tickets
HTTP/1.1 201
etag: "72d232d249fabd6e"
location: /api/v1/tickets/6dfeb4e3-497b-4d62-8270-40dace132b9f
x-request-id: 9073c15c-ea8a-409d-b492-5315e909a71b
x-ratelimit-limit: 60
x-ratelimit-remaining: 57
x-ratelimit-reset: 60
content-type: application/json

{
  "id": "6dfeb4e3-497b-4d62-8270-40dace132b9f",
  "title": "Не работает VPN",
  "description": "",
  "status": "open",
  "created_at": "2026-09-15T21:27:59.538567Z",
  "updated_at": "2026-09-15T21:27:59.538567Z"
}

Разберём этот ответ по частям. Код 201 означает: запрос выполнен, появился новый ресурс. Заголовок location сообщает его адрес — идентификатор выдал сервер, и клиент заранее его не знал. Заголовок etag содержит версию представления: он понадобится, когда обращение будут изменять. Тело содержит созданный объект целиком, включая поля, которые заполнил сервер.

Код отвечает на вопрос «чем кончилось», заголовки — «что с этим делать дальше», тело — «что именно получилось». Клиент, который разбирает только тело, не получает первых двух ответов.

§6Заголовки ответа в учебном проекте

Часть заголовков ответа появляется всегда, часть — только у некоторых операций. Контракт описывает и те, и другие, иначе клиенту неизвестно, на какие заголовки он может рассчитывать.

ЗаголовокКогда появляетсяЧто с ним делает клиент
x-request-idв каждом ответесохраняет в лог, называет в обращении в поддержку
location201 при созданиизапоминает адрес созданного обращения
etagответ с одним обращениемпередаёт обратно в If-None-Match или If-Match
x-ratelimit-*операции записиследит за остатком лимита
retry-after429ждёт указанное число секунд
deprecation, sunsetустаревшие операциипланирует переход на замену
Браузерный клиент видит не всё

Если клиент работает в браузере на другом домене, JavaScript прочитает только те заголовки ответа, которые сервер перечислил в Access-Control-Expose-Headers. Сервер может отправить ETag, а скрипт его не увидит. Разбор — в главе 4.3.

§7Как посмотреть обмен своими глазами

Посмотреть полный обмен на своём компьютере можно двумя способами.

способ 1Терминал
  • виден весь ответ, включая заголовки;
  • легко повторить и вставить в отчёт;
  • флаг -i добавляет заголовки к выводу.
способ 2Swagger UI
  • не нужно набирать адрес и заголовки руками;
  • рядом показаны схема и примеры;
  • кнопка Authorize подставляет токен.
  1. Запустите учебный проектВ папке api-standard-template выполните pip install -r requirements.txt, затем uvicorn app.main:app --reload.
  2. Отправьте запрос из терминалаКоманда ниже читает список обращений и печатает заголовки ответа.
  3. Повторите тот же запрос в Swagger UIОткройте http://127.0.0.1:8000/docs, раскройте GET /api/v1/tickets, нажмите Try it out, затем Execute.
  4. СравнитеВ обоих случаях вы увидите один и тот же код, одинаковые заголовки и одинаковое тело: Swagger UI отправляет обычный HTTP-запрос.
терминал
curl -i "http://127.0.0.1:8000/api/v1/tickets?limit=1"
вывод
HTTP/1.1 200 OK
content-type: application/json
x-request-id: 0b6f3c1e-2a9d-4a4f-9d7e-3f4a1c2b5d6e

{"items":[{"id":"a8d04cb9-…","title":"Не работает VPN",…}],
 "total":3,"limit":1,"offset":0}
Swagger UI после нажатия Execute: строка Request URL, код ответа 404, тело Problem Details и заголовки ответа
Swagger UI, вкладка после Execute. Сверху — адрес, который ушёл на сервер. Ниже — код ответа, тело и, отдельным блоком, заголовки. Это тот же обмен, что и в терминале, только показан по частям.

§8Частые ошибки

Читать только тело ответаКод состояния и заголовки — часть ответа. Клиент, который смотрит только в тело, не заметит ни Location, ни Retry-After, ни предупреждения об устаревании.
Отправлять тело в GETЗапрос на чтение описывается методом и адресом. Тело в нём не передают: часть посредников его отбрасывает, и поведение становится непредсказуемым.
Забыть Content-TypeБез заголовка сервер не знает, как читать тело, и отвечает ошибкой, которая выглядит как «сервер сломался».
Считать Swagger UI отдельным протоколомSwagger UI отправляет обычный HTTP-запрос. Всё, что он показывает, можно повторить в терминале — и наоборот.

§9Проверьте себя

  1. Назовите пять частей запроса и три части ответа. Что из этого может отсутствовать?
  2. Почему при создании обращения сервер возвращает заголовок Location, хотя тело и так содержит созданный объект?
  3. Какие заголовки в ответе учебного сервиса появляются всегда, а какие — только у некоторых операций?
  4. Отправьте POST /api/v1/tickets без заголовка Authorization. Какой код вернёт сервер и что будет в теле?
  5. Повторите один и тот же запрос из терминала и из Swagger UI. Чем отличаются ответы?
ЗапросMETHOD /путь HTTP/1.1Authorization · Content-Typeпустая строкатело (JSON)
ОтветHTTP/1.1 201location · etag · x-request-idпустая строкатело (JSON)
Посмотреть обменcurl -i URL/docs → Try it out → Execute
Дальше1.3 — ресурсы и адреса2.2 — методы4.3 — заголовки контракта

Справочник «Стандартизация HTTP API» · модуль 1 · глава 1.2Макаров Максим Николаевич

Кадр