Справочник «Стандартизация HTTP API» · модуль 1 · API как контракт
Глава 1.2
Один обмен:
запрос и ответ
Все правила темы описывают одно и то же событие — обмен двумя сообщениями. В этой главе оба сообщения разобраны по частям: из чего собран запрос, из чего ответ, что означает каждый заголовок и как посмотреть обмен целиком на своём компьютере.
- Категория
- Основы HTTP
- Уровень
- Начальный
- Связанные главы
- 1.3 Ресурсы и адреса · 2.2 Методы · 2.3 Коды ответов · 4.3 Заголовки контракта
- Зачем это знать
- Чтобы читать чужой запрос и составлять свой, не угадывая смысл частей
§1Обмен — это два сообщения
HTTP устроен как переписка, а не как разговор по телефону. Клиент отправляет одно сообщение — запрос. Сервер отвечает одним сообщением — ответом. После этого обмен закончен: сервер не помнит клиента, а клиент не ждёт продолжения. Следующий запрос того же клиента начинается для сервера с чистого листа.
Из этого свойства следует практическое правило (подробно — глава 2.1): всё необходимое для обработки должно приходить в самом запросе. Нельзя построить контракт «сначала вызови A, сервер запомнит, потом вызови B»: между A и B клиента может обслужить другой экземпляр сервиса, который ничего не запоминал.
Текстовая по форме единица обмена: стартовая строка, набор заголовков, пустая строка и необязательное тело. У запроса стартовая строка содержит метод и адрес, у ответа — код состояния.
Обе стороны собирают сообщение по одинаковым правилам, поэтому запрос и ответ читаются похоже. Разница — в первой строке и в назначении заголовков: в запросе они описывают отправляемое, в ответе — возвращаемое.
§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Заголовки запроса
Заголовки — это сведения о сообщении: кто отправил, в каком формате тело, что клиент готов принять, при каких условиях выполнять операцию. Каждый заголовок решает одну задачу, и путать их нельзя.
Bearer demo-token. Без него операции записи отвечают 401.кто этоapplication/json. Без него сервер видит байты и не знает, как их разбирать.как читать тело412.условиеПервые три заголовка стандартны и встречаются в любом HTTP API. Три последних — часть контракта конкретного сервиса: их нужно объявить в описании, иначе клиент о них не узнает. Разбор каждого — в главе 4.3.
Имена заголовков не чувствительны к регистру: Content-Type и content-type — один и тот же заголовок. В примерах справочника запросы записаны с заглавных букв, а ответы — так, как их печатает сервер, строчными.
§4Тело запроса
Тело содержит данные, которые клиент передаёт серверу. Оно есть не у каждого запроса: GET и DELETE обходятся методом и адресом, а POST и PATCH без тела бессмысленны.
{
"title": "Не работает VPN",
"description": "Ошибка подключения после обновления клиента"
}В теле нет ни идентификатора, ни статуса, ни времени создания — их заполняет сервер. Это часть контракта: схема TicketCreate запрещает лишние поля, и запрос с id получит 422 ещё до того, как дойдёт до кода обработчика.
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 выполняет частичное изменение.
{
"status": "closed"
}§5Ответ: код, заголовки, тело
Ответ начинается не с данных, а с трёхзначного кода состояния. Это сделано для программ: по коду клиент решает, что делать дальше, не разбирая тело. Ниже — настоящий ответ учебного сервиса на создание обращения.
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 | в каждом ответе | сохраняет в лог, называет в обращении в поддержку |
location | 201 при создании | запоминает адрес созданного обращения |
etag | ответ с одним обращением | передаёт обратно в If-None-Match или If-Match |
x-ratelimit-* | операции записи | следит за остатком лимита |
retry-after | 429 | ждёт указанное число секунд |
deprecation, sunset | устаревшие операции | планирует переход на замену |
Если клиент работает в браузере на другом домене, JavaScript прочитает только те заголовки ответа, которые сервер перечислил в Access-Control-Expose-Headers. Сервер может отправить ETag, а скрипт его не увидит. Разбор — в главе 4.3.
§7Как посмотреть обмен своими глазами
Посмотреть полный обмен на своём компьютере можно двумя способами.
- виден весь ответ, включая заголовки;
- легко повторить и вставить в отчёт;
- флаг
-iдобавляет заголовки к выводу.
- не нужно набирать адрес и заголовки руками;
- рядом показаны схема и примеры;
- кнопка Authorize подставляет токен.
- Запустите учебный проектВ папке
api-standard-templateвыполнитеpip install -r requirements.txt, затемuvicorn app.main:app --reload. - Отправьте запрос из терминалаКоманда ниже читает список обращений и печатает заголовки ответа.
- Повторите тот же запрос в Swagger UIОткройте
http://127.0.0.1:8000/docs, раскройтеGET /api/v1/tickets, нажмите Try it out, затем Execute. - СравнитеВ обоих случаях вы увидите один и тот же код, одинаковые заголовки и одинаковое тело: 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}
§8Частые ошибки
Location, ни Retry-After, ни предупреждения об устаревании.§9Проверьте себя
- Назовите пять частей запроса и три части ответа. Что из этого может отсутствовать?
- Почему при создании обращения сервер возвращает заголовок
Location, хотя тело и так содержит созданный объект? - Какие заголовки в ответе учебного сервиса появляются всегда, а какие — только у некоторых операций?
- Отправьте
POST /api/v1/ticketsбез заголовкаAuthorization. Какой код вернёт сервер и что будет в теле? - Повторите один и тот же запрос из терминала и из 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 → Execute1.3 — ресурсы и адреса2.2 — методы4.3 — заголовки контрактаСправочник «Стандартизация HTTP API» · модуль 1 · глава 1.2Макаров Максим Николаевич