Справочник «Стандартизация HTTP API» · модуль 1 · API как контракт
Глава 1.4
Учебный стенд
Service Desk API
Все последующие главы опираются на один работающий сервис. В этой главе он запускается: разобрано, из чего состоит репозиторий, как отправить первый запрос из браузера и из терминала и как отличить поломку стенда от ошибки в запросе.
- Категория
- Рабочее место
- Уровень
- Начальный
- Что понадобится
- Python 3.12, терминал, браузер; Node.js — только для линтера в модуле 6
- Результат
- Запущенный сервис, открытый Swagger UI и первый успешный запрос
§1Что это за сервис
Service Desk API — учебный сервис регистрации обращений в службу поддержки. Сотрудник сообщает о проблеме: «не работает VPN», «принтер в 312 печатает пустые листы». Каждое сообщение становится обращением с заголовком, описанием, статусом и временем создания. Специалист поддержки меняет статус: open → in_progress → closed.
Область выбрана так, чтобы в ней было всё, ради чего существуют правила API, и ничего лишнего. Здесь есть коллекция и отдельный объект, фильтрация и постраничный вывод, создание, частичное изменение, удаление, ошибки «не найдено», «нет прав» и «данные не по схеме». При этом нет сложной бизнес-логики, которая отвлекала бы от контракта.
Данные живут в памяти процесса: после перезапуска остаются три демонстрационных обращения. База данных здесь не нужна: контракт API от способа хранения не зависит.
Репозиторий: github.com/MaximBytecamp/api-standard-template. Он помечен как шаблон: кнопка Use this template создаёт вашу копию — она понадобится в модуле 8. Копия проекта есть и в репозитории курса: standartizaciya-sertifikaciya-i-dokumentovedenie/api-standard-template/.
§2Запуск за четыре шага
- Получите проектСкачайте архив по ссылке выше (Code → Download ZIP) или выполните
git clone https://github.com/MaximBytecamp/api-standard-template.git. Откройте папку в терминале. - Создайте окружениеВиртуальное окружение изолирует библиотеки проекта от системного Python.
- Установите зависимостиВерсии закреплены в
requirements.txt: у всех участников курса будет одинаковое поведение. - Запустите серверФлаг
--reloadперезапускает сервис при сохранении файла — это пригодится, когда вы начнёте менять код.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
uvicorn app.main:app --reloadINFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: Started reloader process using WatchFiles INFO: Application startup complete.
Откройте в браузере http://127.0.0.1:8000/docs. Должна появиться страница Swagger UI: название сервиса, его версия и список операций, сгруппированных тегом Tickets.
/openapi.json и кнопка Authorize. Ниже — группа Tickets и пять операций. Кадр снят на версии 1.0.0; в 1.1.0 у операций появились дополнительные заголовки.§3Что внутри репозитория
Кода в проекте меньше, чем правил и проверок. Репозиторий устроен так намеренно: в этой дисциплине разбирается не реализация сервиса, а правила, по которым он описан и проверяется.
api-standard-template/
main.py — метаданные и сборка описания, schemas.py — схемы запросов и ответов, problems.py — формат ошибок, api/v1/endpoints/tickets.py — пять операцийAPI_STYLE_GUIDE.md — правила проекта, STANDARDS.md — источник каждого правила с цитатой и переводом, VERSIONING.md, ERRORS.md, SECURITY.md, QUICKSTART.md, adr/openapi.json и openapi.yaml — то, что читают инструменты и другие командыtest_tickets.py — поведение операций, test_http_features.py — заголовки контракта, test_contract.py — правила описанияexport_openapi.py — выгрузка и сверка контракта с кодом, build_docs.py — страницы видов ошибокworkflows/ci.yml — проверки, workflows/docs.yml — публикация документации, CODEOWNERS, шаблон pull request, формы issueК каждой из этих папок мы вернёмся: openapi/ — в модуле 3, docs/ — в модуле 6, .github/ — в модуле 7.
§4Пять операций и два токена
Чтение открыто всем. Операции записи требуют токен — так в учебном сервисе показана разница между «сервер не знает, кто вы» и «сервер знает и не разрешает».
| Токен | Чтение | Создание, изменение, удаление |
|---|---|---|
| не передан | да | 401 Unauthorized |
demo-token | да | да |
viewer-token | да | 403 Forbidden |
В Swagger UI токен вводится один раз: кнопка Authorize в шапке, поле Value, затем Authorize и Close. После этого интерфейс сам добавляет заголовок Authorization к запросам, у которых в контракте объявлена схема безопасности — у них в списке стоит значок замка.
BearerAuth, её тип — http, механизм — bearer. Это не настройка интерфейса, а часть описания API: Swagger UI построил окно по разделу components.securitySchemes.Два статических токена в переменных окружения — учебное упрощение. В рабочем контуре токен выдаёт сервер авторизации по OAuth 2.0 или OpenID Connect, а сервис проверяет подпись и срок действия. Но разницу между 401 и 403 удобнее увидеть на простом стенде.
§5Первый запрос
Прочитаем список обращений. Чтение токена не требует, поэтому команда короткая:
curl -i "http://127.0.0.1:8000/api/v1/tickets?limit=2"HTTP/1.1 200 OK
content-type: application/json
x-request-id: 0b6f3c1e-2a9d-4a4f-9d7e-3f4a1c2b5d6e
{"items": [ … ], "total": 3, "limit": 2, "offset": 0}Теперь создадим обращение. Здесь уже нужен токен, а вместе с ним — заголовок формата и тело:
curl -i -X POST http://127.0.0.1:8000/api/v1/tickets \ -H "Authorization: Bearer demo-token" \ -H "Content-Type: application/json" \ -d '{"title": "Не работает VPN", "description": "После обновления клиента"}'
В ответе придёт код 201, заголовок location с адресом нового обращения и его тело. Попробуйте повторить ту же команду без строки с Authorization — сервер ответит 401 и телом в формате Problem Details. С токеном viewer-token ответ будет 403: клиент известен, но записывать ему нельзя.
Три ответа — 201, 401, 403 — на один и тот же запрос с разными токенами. Это и есть контракт в действии: поведение предсказуемо и описано заранее.
§6Что проект проверяет о себе сам
В репозитории лежат три проверки. Запустите их сразу после установки: так вы убедитесь, что окружение собрано правильно, и заодно увидите, что именно проверяется.
pytest python scripts/export_openapi.py --check npx @stoplight/spectral-cli lint openapi/openapi.yaml
............................... [100%]
31 passed in 0.08s
# файлы описания совпадают с кодом — команда завершилась без сообщений
No results with a severity of 'warn' or higher found!operationId, формат ошибок.31 тестopenapi/openapi.yaml с описанием, которое генерирует код. Разошлись — значит, кто-то изменил код и забыл выгрузить контракт.против расхождения.spectral.yaml: контакты, теги, snake_case, формат ошибок, отсутствие глаголов в адресах.линтерТе же три шага выполняет GitHub Actions при каждом изменении — об этом модуль 6. Запуск их локально до push экономит время: ошибку видно сразу, а не через две минуты в интерфейсе GitHub.
§7Если что-то не работает
Ниже — проблемы, которые встречаются чаще всего, и признак, по которому каждую из них узнают.
| Что видно | Причина | Что сделать |
|---|---|---|
Address already in use при запуске | порт 8000 занят другим процессом | запустить на другом порту: uvicorn app.main:app --port 8001 |
ModuleNotFoundError: No module named 'app' | сервер запущен не из папки проекта | перейти в папку api-standard-template или добавить --app-dir |
/docs открывается, /redoc пустой | браузер не смог загрузить скрипт ReDoc с CDN | проверить подключение к сети; в проекте версия ReDoc закреплена явно |
401 на создание | не передан заголовок Authorization | в Swagger UI нажать Authorize, в curl добавить -H "Authorization: Bearer demo-token" |
422 на создание | тело не соответствует схеме: нет title, он короче трёх символов или передано лишнее поле | посмотреть массив errors в теле ответа: там указано поле и причина |
| Изменения в коде не видны | сервер запущен без --reload | перезапустить с флагом или вручную |
Сначала смотрите на код ответа, потом на тело, потом в лог сервера. Код 4xx означает, что исправлять нужно запрос, 5xx — что смотреть надо в лог сервера. Идентификатор из заголовка x-request-id есть и в ответе, и в записи лога: по нему находят нужный запрос.
§8Частые ошибки
venv версии библиотек смешиваются с другими проектами, и поведение перестаёт совпадать с описанным в справочнике.uvicorn ищет пакет app относительно текущей папки. Сообщение ModuleNotFoundError означает именно это, а не поломку проекта.python scripts/export_openapi.py, иначе проверка --check найдёт расхождение.§9Проверьте себя
- Запустите сервис и откройте
/docs. Какая версия контракта указана в шапке? - Создайте обращение из Swagger UI и найдите в ответе заголовок
location. Откройте этот адрес в браузере. - Повторите создание без токена и с токеном
viewer-token. Какие коды вернул сервер и чем отличаются тела ответов? - Запустите три проверки проекта. Что именно сообщает каждая из них?
- Измените в
app/main.pyназвание сервиса, обновите/docs, затем выполнитеpython scripts/export_openapi.py --check. Что произойдёт и почему?
python -m venv .venvpip install -r requirements.txtuvicorn app.main:app --reload/docs — Swagger UI/redoc — ReDoc/openapi.json — контрактdemo-token — записьviewer-token — только чтениеpytestexport_openapi.py --checkspectral lint openapi/openapi.yamlСправочник «Стандартизация HTTP API» · модуль 1 · глава 1.4Макаров Максим Николаевич