Перейти к основному содержимому

Практикум REST и WebSocket — итоги

Кратко — что стоит унести из практикума OrderDesk: два сервиса на портах 8100 и 5200, контракт /api/v1, межсервисный REST и WebSocket к клиенту. Если ответ в FAQ кажется общим — откройте шаг из маршрута или оглавление раздела. Для самопроверки — чек-лист.


FAQ — Часто задаваемые вопросы

Типичные сбои OrderDesk и ответы на частые запросы в поиске ("REST API что это", "JWT как работает", "WebSocket vs polling", "тест API в Postman"). Краткий ответ здесь; разбор — в шагах практикума. Заучивание — в чек-листе.

Вопрос. Postman или curl пишет "connection refused" на localhost:8100.

Ответ. catalog-api не слушает порт: поднимите FastAPI (uvicorn app.main:app --reload --port 8100 из каталога проекта), проверьте firewall и что порт не занят другим процессом. Swagger http://localhost:8100/docs должен открываться в браузере. Подробнее — шаг 4.

Вопрос. "Connection refused" на localhost:5200 при создании заказа.

Ответ. orders-api не запущен: dotnet run в папке сервиса, в логах — Now listening on: http://localhost:5200. Каталог может быть жив, но без orders-api JWT и POST /orders недоступны. Подробнее — шаг 5.

Вопрос. POST /api/v1/orders возвращает 401 Unauthorized.

Ответ. Для клиентских ручек нужен Bearer JWT: сначала POST /api/v1/auth/token, затем заголовок Authorization: Bearer {{access_token}}. Пустое окружение Postman или просроченный токен дают тот же 401. Подробнее — шаг 6, шаг 8.

Вопрос. POST /api/v1/reservations в каталоге отвечает 401 — ключ вроде указан.

Ответ. Межсервисные вызовы требуют заголовок X-Api-Key, совпадающий с CATALOG_API_KEY (Python) и Catalog:ApiKey (C#). Опечатка, пробел в конце строки или разные значения в двух appsettings/.env — частая причина. Сравнивайте через secrets.compare_digest на стороне каталога. Подробнее — шаг 6.

Вопрос. Клиент получает 403 Forbidden на GET /api/v1/orders/{id}.

Ответ. JWT валиден, но субъект токена не совпадает с владельцем заказа (проверка sub / user id). В учебном стенде залогиньтесь тем же пользователем, что создавал заказ, или ослабьте политику только в dev. Подробнее — шаг 5.

Вопрос. POST /api/v1/orders возвращает 502 Bad Gateway.

Ответ. orders-api не достучался до catalog-api (сервис остановлен, неверный Catalog:BaseUrl, таймаут HttpClient). Ожидайте Problem Details с кодом 502, а не необработанное 500. Проверьте http://localhost:8100/health и логи обоих процессов. Подробнее — шаг 5, интерактив "Catalog down".

Вопрос. Резерв или заказ завершается 409 Conflict.

Ответ. Нехватка остатка (stockAvailable) или конфликт бизнес-правила. В негативном сценарии Postman quantity: 9999 как раз должен дать 409. Повтор с тем же телом без идемпотентности не должен списывать остаток дважды — см. Idempotency-Key. Подробнее — шаг 2, шаг 4.

Вопрос. Браузерный фронтенд пишет "blocked by CORS policy", Postman — 200.

Ответ. CORS проверяет только браузер: origin фронта должен быть в списке WithOrigins на orders-api (и при прямых вызовах каталога — на FastAPI). Postman CORS не эмулирует. Проверьте preflight OPTIONS и заголовки Access-Control-*. Подробнее — шаг 6.

Вопрос. Раньше работало, сейчас 401 — "JWT expired" или invalid signature.

Ответ. Access token короткоживущий: запросите новый через POST /auth/token. При смене Jwt:SigningKey в конфиге все старые токены станут невалидными. Сверьте Issuer, Audience и часы на машине (clock skew). Подробнее — шаг 6.

Вопрос. WebSocket в Postman подключается и сразу обрывается.

Ответ. URL должен быть ws://localhost:5200/ws/orders?access_token=<jwt> с свежим токеном. Пустой или просроченный access_token в query даёт закрытие с 401. Убедитесь, что orders-api запущен и middleware JWT читает query (см. OnMessageReceived). Подробнее — шаг 7.

Вопрос. После POST /orders в WebSocket нет события order.status_changed.

Ответ. Подключение WS должно быть до или сразу после создания заказа, под тем же sub, что в JWT. Hub рассылает только подписчикам пользователя; проверьте JSON type и версию v. В логах orders-api — факт вызова BroadcastAsync. Подробнее — шаг 7, шаг 8.

Вопрос. Остановил catalog-api — заказы всё равно 201, ожидал 502.

Ответ. Возможен кэш/мок в Postman, запрос ушёл не на локальный orders_base, или в коде нет ветки Problem Details при ошибке HttpClient. Повторите с выключенным uvicorn и catalog_base = http://localhost:8100. В песочнице на intro сценарий "Catalog down" показывает ожидаемый 502.

Вопрос. orders-api в логах "Invalid API key", хотя в Postman ключ верный.

Ответ. Ключ в Postman для ручного вызова каталога (api_key) должен совпадать с тем, что orders-api шлёт исходящим X-Api-Key. Это два разных места: переменная окружения Postman и Catalog:ApiKey в appsettings.Development.json. Подробнее — шаг 8.

Вопрос. В Postman {{product_id}} подставляется пустым.

Ответ. Выбрано окружение OrderDesk Local, в Tests запроса "создать товар" есть pm.environment.set("product_id", body.id) и статус 201. Без активного environment переменные не сохраняются. Порядок запросов в коллекции — как в шаге 8.

Вопрос. Повтор POST /reservations с тем же Idempotency-Key — что должно быть?

Ответ. Идемпотентность: второй запрос с тем же ключом возвращает тот же reservationId и 201 (или 200 по вашей реализации), без повторного списания остатка. Без ключа сеть после таймаута может создать дубликат. Подробнее — шаг 4, шаг 6.

Вопрос. Два одинаковых заказа после обрыва сети на POST /orders.

Ответ. Клиент повторил запрос без стабильного Idempotency-Key / requestId. В практикуме ключ резерва строится как {orderId}:{productId} на стороне orders-api — при повторе оркестрации каталог не должен резервировать дважды. Добавьте ключ на уровне создания заказа в проде. Подробнее — шаг 5.

Вопрос. Swagger каталога открывается, orders-api пишет timeout к catalog.

Ответ. В Catalog:BaseUrl опечатка (https вместо http, лишний слэш, порт 8000 вместо 8100), Docker hostname catalog без compose-сети. С хоста Windows/Mac для локального запуска обычно http://localhost:8100. Подробнее — шаг 5.

Вопрос. 422 Unprocessable Entity на POST /api/v1/products.

Ответ. Тело не проходит Pydantic-схему: отрицательный price, отсутствует sku, неверные типы. Сверьте JSON с контрактом в шаге 2 и примером в шаге 8.

Вопрос. В логах catalog и orders разный X-Request-Id для одного заказа.

Ответ. Сквозная трассировка: клиент (или Pre-request Script коллекции) задаёт X-Request-Id, orders-api пробрасывает его в HttpClient к каталогу. Без проброса разбор инцидента затруднён. Подробнее — шаг 6, шаг 8.

Вопрос. GET /api/v1/products из браузера с другого порта — CORS, из orders-api — нет.

Ответ. Публичные GET каталога в учебной модели могут быть открыты, но браузер всё равно требует CORS при cross-origin. Сервер-сервер (HttpClient) CORS не использует. Настройте политику или проксируйте через orders-api. Подробнее — шаг 6.

Вопрос. WebSocket ping/pong — зачем, если TCP keep-alive есть?

Ответ. Прокси и балансировщики могут рвать "тихие" долгие WS; прикладной ping/pong в JSON подтверждает живость канала и логику hub. Подробнее — шаг 7.

Вопрос. dotnet run слушает другой порт, не 5200.

Ответ. Проверьте launchSettings.json, ASPNETCORE_URLS и явный app.Run("http://localhost:5200") из практикума. Postman orders_base должен совпадать с фактическим URL. Подробнее — шаг 5.

Вопрос. uvicorn: Error loading ASGI app — модуль не найден.

Ответ. Команду запускайте из корня проекта catalog-api, где лежит пакет app, с активированным venv и установленными зависимостями (pip install -r requirements.txt). Путь app.main:app чувствителен к структуре каталогов. Подробнее — шаг 4.

Вопрос. DTO в ответе заказа не совпадает с телом запроса — "сломали контракт"?

Ответ. Ответ 201 часто обогащён полями (status, lines, id резерва). Сверьте OpenAPI и таблицу маппинга entity ↔ DTO в шаге 3. Клиент парсит только документированные поля.

Вопрос. Newman/CI падает на негативных тестах 409/502.

Ответ. В папке Negative в Tests должен быть assert на ожидаемый статус (pm.response.to.have.status(409)), а не на 201. Для 502 каталог перед прогоном останавливают. Подробнее — шаг 8.

Вопрос. Песочница на intro показывает 502, локальный код — 500 без тела.

Ответ. Доведите обработчик исключений HttpClient до Problem Details и кода 502, как в контракте шага 2. Необработанное исключение ASP.NET даёт 500. Сверьтесь с чек-листом в шаге 5.

Вопрос. Где теория про REST, JWT и WebSocket вне этого практикума?

Ответ. 8.05 Микросервисы и интеграция — REST, проектирование API, реактивные транспорты; HTTP — 2.09. Практикум 8.08 — применение на OrderDesk.

Вопрос. REST API — что это простыми словами?

Ответ. REST — стиль построения HTTP API вокруг ресурсов (товар, заказ) и методов GET/POST/DELETE с предсказуемыми кодами ответа. В OrderDesk контракт зафиксирован под префиксом /api/v1. Теория — 8.05 intro, практика — шаг 2.

Вопрос. Чем REST отличается от SOAP и gRPC для учебного проекта?

Ответ. REST + JSON проще отлаживать в Postman и браузере; SOAP и gRPC требуют отдельных контрактов и стеков. Практикум 8.08 сознательно на HTTP/JSON между catalog-api и orders-api. Сравнение подходов — 8.05 intro, ресурсы OrderDesk — шаг 1.

Вопрос. WebSocket и polling — что выбрать для статуса заказа?

Ответ. Polling (GET /orders/{id} раз в N секунд) нагружает API и даёт задержку. WebSocket держит канал и шлёт order.status_changed сразу после смены статуса — так устроен UI в практикуме. Протокол событий — шаг 7, сценарий — intro.

Вопрос. JWT — как работает access token в OrderDesk?

Ответ. Клиент получает JWT через POST /api/v1/auth/token, затем передаёт его в заголовке Authorization: Bearer … или в query WebSocket. Сервер проверяет подпись, exp, iss, aud и субъект sub для доступа к заказам. Настройка — шаг 6, проверка — шаг 8.

Вопрос. Bearer token в Postman — куда вставлять для REST?

Ответ. Вкладка Authorization → Bearer Token, значение {{access_token}} после запроса "получить токен". Для ручек каталога от клиента JWT обычно не нужен; межсервисно — отдельно X-Api-Key. Коллекция — шаг 8, политика — шаг 6.

Вопрос. Как тестировать REST API в Postman с нуля?

Ответ. Импортируйте коллекцию OrderDesk, активируйте окружение OrderDesk Local, поднимите catalog (8100) и orders (5200), пройдите цепочку: товар → токен → заказ → WS. Пошагово — шаг 8, оглавление — intro.

Вопрос. OpenAPI и Swagger — зачем /docs на каталоге?

Ответ. OpenAPI описывает схемы запросов/ответов; Swagger UI на http://localhost:8100/docs даёт интерактивные вызовы без Postman. Контракт /api/v1 согласован в шаге 2, реализация FastAPI — шаг 4.

Вопрос. HTTP 401 и 403 — в чём разница в API заказов?

Ответ. 401 — нет или просрочен JWT (не аутентифицирован). 403 — токен валиден, но субъект не владелец ресурса (доступ запрещён). Коды заложены в контракт шага 2, проверка владельца — шаг 5.

Вопрос. Idempotency-Key на POST — зачем нужен в API?

Ответ. Повтор сети после таймаута не должен дважды списать остаток или создать второй заказ. Ключ связывает повтор с тем же результатом. Резерв в каталоге — шаг 4, оркестрация — шаг 5.

Вопрос. X-Api-Key между микросервисами — это замена JWT?

Ответ. JWT — для внешнего клиента (браузер, Postman от лица пользователя). X-Api-Key — доверенный канал orders-api → catalog-api, без пользовательского контекста. Сравнение секретов — шаг 6, сценарий OrderDesk — шаг 1.

Вопрос. Зачем в практикуме два сервиса на портах 8100 и 5200?

Ответ. catalog-api владеет остатками, orders-api владеет заказами и оркестрирует резерв по REST — так видна граница микросервисов и типичные сбои (502, ключи). Архитектура — шаг 1, теория границ — 8.05 intro.

Вопрос. Problem Details (RFC 7807) — что клиент видит при 502?

Ответ. Структурированное тело с type, title, status вместо пустого 500 — orders-api сообщает, что каталог недоступен, а не "сломался сам". Контракт ошибок — шаг 2, обработчик — шаг 5.

Вопрос. CORS policy в браузере — почему Postman работает без настройки?

Ответ. CORS проверяет только браузер при cross-origin; Postman и curl ограничений origin не имеют. Для фронта на другом порту настройте WithOrigins на orders-api. Подробнее — шаг 6, интерактив — intro.

Вопрос. Оркестрация заказа через REST — какие шаги между сервисами?

Ответ. POST /orders в orders-api → POST /reservations в catalog с API-ключом → ответ клиенту → push статуса по WebSocket. Диаграмма потоков — шаг 1, код C# — шаг 5.

Вопрос. FastAPI и ASP.NET Core — почему два языка в одном практикуме?

Ответ. Учебный стенд показывает реальную интеграцию разнородных команд: Python-каталог и C#-заказы по общему HTTP-контракту. Каталог — шаг 4, заказы — шаг 5.

Вопрос. Версия API /api/v1 — зачем префикс в URL?

Ответ. Позволяет выпустить v2 без поломки старых клиентов; все ручки практикума живут под одной версией. Проектирование ресурсов — шаг 2, маппинг DTO — шаг 3.

Вопрос. HttpClient из C# к Python — типичные ошибки BaseUrl?

Ответ. Неверный порт (8000 вместо 8100), https локально, лишний слэш, hostname catalog без Docker-сети — приводят к таймауту и 502 на клиенте. Настройка Catalog:BaseUrlшаг 5, health каталога — шаг 4.

Вопрос. WebSocket ws://localhost:5200/ws/orders — когда подключаться?

Ответ. До или сразу после POST /orders, с свежим access_token в query — иначе hub закроет соединение. Протокол JSON и heartbeat — шаг 7, проверка в Postman — шаг 8.

Вопрос. Событие order.status_changed — что слать клиенту вместо опроса?

Ответ. JSON с полями type, v, id заказа и новый status — UI обновляется без периодического GET. Hub и broadcast — шаг 7, E2E — шаг 8.

Вопрос. С чего начать практикум REST и WebSocket 8.08?

Ответ. Прочитайте intro и шаг 1, затем контракт шаг 2, поднимите сервисы 45, закройте чек-лист. База по интеграциям — 8.05 intro.

Вопрос. Newman CLI — как гонять коллекцию Postman в CI?

Ответ. Экспорт коллекции и окружения, newman run collection.json -e env.json — удобно для регрессии 409/502 в папке Negative. Скрипты Tests — шаг 8, устойчивость — шаг 6.

Вопрос. DTO и entity в REST — почему ответ не копия запроса?

Ответ. DTO скрывает внутренние поля БД и добавляет вычисляемые (status, строки заказа). Клиент опирается только на документированный JSON. Маппинг — шаг 3, OpenAPI — шаг 2.


Резюме раздела

  • catalog-api (Python, FastAPI, порт 8100) — единственный владелец остатков: товары, POST/DELETE резервов, SQLite в учебной сборке, входящий X-Api-Key и Idempotency-Key на резерве.
  • orders-api (C#, ASP.NET Core 8, порт 5200) — заказы, оркестрация резерва по REST, JWT для внешних клиентов, 502 при недоступном каталоге.
  • JWT — короткий access token после POST /api/v1/auth/token; для WebSocket тот же токен в query access_token; ключ подписи из окружения, не из git.
  • API-ключ — только межсервисный канал orders → catalog (X-Api-Key), сравнение через secrets.compare_digest, ротация двумя активными ключами в проде.
  • WebSocketws://localhost:5200/ws/orders, JSON-события order.status_changed, ping/pong, push статусов без polling UI.

Маршрут по статьям

ШагСтатьяСодержание
1Сценарий и архитектураOrderDesk, границы сервисов, диаграмма потоков
2Проектирование контракта APIРесурсы, HTTP-коды, OpenAPI
3Модели данных и маппингDTO, JSON, версии полей
4Сервис каталога на PythonFastAPI, резервы, идемпотентность
5Сервис заказов на C#Minimal API, HttpClient, заказы
6Безопасность и устойчивостьJWT, API-ключ, CORS, таймауты
7WebSocket и событияHub, протокол сообщений, heartbeat
8Проверка в PostmanКоллекция, E2E, негативные кейсы

Проверьте себя: Чек-лист самопроверки.


Следующие шаги


См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").