Skip to content

Service Gateway

plutogr edited this page May 23, 2026 · 2 revisions

Сервис API Gateway (TravelAgency.Gateway)

Подробное описание реализованного API Gateway: назначение, стек, конфигурация, маршрутизация, безопасность и наблюдаемость.


Содержание

  1. Назначение и роль в архитектуре
  2. Стек и зависимости
  3. Структура проекта
  4. Конфигурация (appsettings)
  5. Pipeline запроса: порядок middleware
  6. Маршрутизация (YARP)
  7. Аутентификация и авторизация
  8. Rate limiting
  9. CORS
  10. Health checks
  11. Наблюдаемость
  12. Окружения: Development и Production
  13. Запуск и развёртывание

1) Назначение и роль в архитектуре

TravelAgency.Gateway — единая точка входа для всех клиентских запросов к бэкенду. Сервис:

  • Принимает запросы по единому префиксу /api/v1/....
  • Проверяет JWT, применяет политики доступа (RBAC), лимиты запросов и CORS.
  • Проксирует запросы к соответствующим бэкенд-сервисам (Identity, Catalog, Booking, Chat, Media) с преобразованием пути.

Собственной БД у Gateway нет; бизнес-логика в нём не размещается — только маршрутизация, проверка токена и политики доступа (в соответствии с архитектурными принципами).


2) Стек и зависимости

Компонент Технология
Платформа .NET 10, ASP.NET Core
Обратное проксирование YARP (Yet Another Reverse Proxy) 2.3
Аутентификация JWT Bearer (Microsoft.AspNetCore.Authentication.JwtBearer)
Ограничение частоты запросов AspNetCoreRateLimit 5.0 (in-memory)
Логирование Serilog.AspNetCore 10
Трейсинг OpenTelemetry (AspNetCore + Hosting)
Общие контракты TravelAgency.Shared.Contracts (роли, политики)

3) Структура проекта

src/Gateway/TravelAgency.Gateway/
├── Program.cs                 # Точка входа, регистрация сервисов и pipeline
├── TravelAgency.Gateway.csproj
├── appsettings.json           # Конфиг по умолчанию (production-адреса сервисов)
├── appsettings.Development.json  # Переопределение для локальной разработки
├── Dockerfile
├── Extensions/
│   ├── AuthenticationExtensions.cs   # JWT + политики авторизации
│   ├── CorsExtensions.cs             # CORS-политика
│   ├── HealthCheckExtensions.cs      # Health checks + маппинг эндпоинтов
│   ├── ObservabilityExtensions.cs    # Serilog + OpenTelemetry
│   ├── RateLimitingExtensions.cs     # IpRateLimit
│   └── YarpExtensions.cs             # Регистрация и маппинг YARP
├── HealthChecks/
│   └── DownstreamServiceHealthCheck.cs  # HTTP-проверка доступности бэкендов
└── Middleware/
    ├── CorrelationIdMiddleware.cs       # Correlation ID для трассировки
    └── GlobalExceptionHandlerMiddleware.cs  # Глобальная обработка ошибок (ProblemDetails)

4) Конфигурация (appsettings)

Основные секции:

Секция Назначение
Serilog Уровни логирования, шаблон вывода (в т.ч. {CorrelationId}), обогащение логов
JwtSettings Issuer, Audience, SigningKey (≥32 символа), ValidateLifetime — для проверки JWT
ReverseProxy Маршруты (Routes) и кластеры (Clusters) YARP — куда и как проксировать
IpRateLimiting Лимиты по IP/эндпоинту, заголовки X-Forwarded-For / X-ClientId
Cors AllowedOrigins, AllowedMethods, AllowedHeaders
HealthCheckEndpoints URL эндпоинтов /health/live каждого бэкенда для readiness

В Development переопределяются адреса кластеров (localhost:5010–5050), разрешённые origins (localhost:3000, 5173) и URL для health checks.


5) Pipeline запроса: порядок middleware

Порядок в Program.cs жёстко задан и влияет на поведение:

№ Компонент Назначение
1 Serilog Request Logging Логирование входящего запроса
2 CorrelationIdMiddleware Чтение/генерация X-Correlation-Id, запись в TraceIdentifier и в ответ, добавление в лог-контекст
3 GlobalExceptionHandlerMiddleware Try/catch всего pipeline; при ошибке — ответ 500 в формате ProblemDetails (RFC 7807)
4 CORS Проверка Origin, методов и заголовков по политике Gateway
5 IpRateLimiting Проверка лимитов; при превышении — 429
6 Authentication Валидация JWT (если маршрут не Anonymous)
7 Authorization Проверка политики, привязанной к маршруту YARP
— MapHealthChecks Эндпоинты /health/live и /health/ready (обрабатываются до YARP)
— MapReverseProxy (YARP) Выбор маршрута по пути/методу, трансформация пути, проксирование на бэкенд

Итог: сначала идентификация запроса и обработка ошибок, затем CORS и лимиты, затем auth, затем маршрутизация на бэкенды.


6) Маршрутизация (YARP)

Внешний API для клиента — единый префикс /api/v1. YARP по пути и методу выбирает маршрут, убирает префикс /api/v1, добавляет префикс сервиса и отправляет запрос в нужный кластер.

Маршруты (Routes)

Имя маршрута Внешний путь HTTP-методы Политика доступа Кластер (бэкенд)
auth-route /api/v1/auth/{**catch-all} все Anonymous identity-cluster
catalog-public-route /api/v1/catalog/{**catch-all} GET Anonymous catalog-cluster
catalog-manage-route /api/v1/catalog/{**catch-all} POST, PUT, PATCH, DELETE RequireManagerOrAdmin catalog-cluster
bookings-route /api/v1/bookings/{**catch-all} все RequireClient booking-cluster
favorites-route /api/v1/favorites/{**catch-all} все RequireClient booking-cluster
chat-route /api/v1/chat/{**catch-all} все RequireClient chat-cluster
media-route /api/v1/media/{**catch-all} все RequireClient media-cluster

Трансформация пути (Transforms)

Для каждого маршрута заданы преобразования:

  1. PathRemovePrefix: /api/v1 — удаляется.
  2. PathPrefix: добавляется префикс сервиса (/identity, /catalog, /bookings, /favorites, /chat, /media).

Примеры:

  • POST /api/v1/auth/login → Identity Service: POST /identity/login
  • GET /api/v1/catalog/tours → Catalog Service: GET /catalog/tours
  • GET /api/v1/bookings/my → Booking Service: GET /bookings/my

Кластеры (Clusters)

В production адреса задаются в appsettings.json (имена контейнеров Docker, порт 8080):

  • identity-cluster → http://identity-service:8080
  • catalog-cluster → http://catalog-service:8080
  • booking-cluster → http://booking-service:8080
  • chat-cluster → http://chat-service:8080
  • media-cluster → http://media-service:8080

В development те же кластеры переопределяются в appsettings.Development.json на http://localhost:5010 … 5050.


7) Аутентификация и авторизация

Аутентификация (JWT)

  • Схема по умолчанию: JwtBearer.
  • Параметры валидации: Issuer, Audience, SigningKey (минимум 32 символа), проверка срока жизни токена, роль из claim Role.
  • Конфигурация в секции JwtSettings; при отсутствии или коротком ключе приложение не запускается.

Политики авторизации

Политики заданы в AuthenticationExtensions.AddGatewayAuthorization и используют константы из TravelAgency.Shared.Contracts:

Политика Роли
RequireClient Client, Manager, Admin
RequireManager Manager, Admin
RequireAdmin Admin
RequireManagerOrAdmin Manager, Admin

Привязка политики к маршруту задаётся в конфиге YARP (AuthorizationPolicy у каждого route). Маршруты auth и публичного каталога (GET) используют Anonymous; остальные — соответствующую политику (см. таблицу маршрутов выше).


8) Rate limiting

  • Библиотека: AspNetCoreRateLimit, хранилище — in-memory (MemoryCache).
  • Идентификация клиента: по реальному IP из заголовка X-Forwarded-For и при необходимости X-ClientId (настраивается в IpRateLimiting).
  • При превышении лимита возвращается HTTP 429.

Пример правил из конфига:

  • Общий лимит: 100 запросов в 1 минуту на все эндпоинты (*).
  • POST /api/v1/auth/login: 20 запросов в 1 минуту.
  • POST /api/v1/auth/register: 10 запросов в 1 минуту.

Дополнительные правила можно задать в секции IpRateLimitPolicies.


9) CORS

  • Имя политики: GatewayPolicy (константа CorsExtensions.GatewayCorsPolicyName).
  • В production по умолчанию разрешён origin: https://travelagency.com.
  • В Development дополнительно разрешены: http://localhost:3000, http://localhost:5173.
  • Разрешённые методы и заголовки (в т.ч. Authorization, Content-Type, X-Correlation-Id) и AllowCredentials() задаются в секции Cors и применяются ко всем ответам при использовании этой политики.

10) Health checks

  • Liveness — GET /health/live: предикат отключает все проверки; эндпоинт всегда отвечает «жив» (для оркестраторов/Kubernetes).
  • Readiness — GET /health/ready: выполняются проверки с тегом ready. Для каждого URL из HealthCheckEndpoints регистрируется DownstreamServiceHealthCheck: HTTP GET к эндпоинту /health/live соответствующего бэкенда с таймаутом 5 секунд. Результат — Healthy (2xx) или Degraded (ошибка/исключение).

Таким образом, readiness Gateway показывает, доступны ли все настроенные бэкенд-сервисы.


11) Наблюдаемость

  • Логи (Serilog): настройка из конфига, в шаблон вывода в консоль добавлен {CorrelationId}. Обогащение: FromLogContext, WithMachineName, WithEnvironmentName.
  • Трейсинг (OpenTelemetry): сервис регистрируется как TravelAgency.Gateway, включена инструментация ASP.NET Core. Экспорт в Jaeger/OTLP настраивается отдельно при необходимости.
  • Correlation ID: передаётся в заголовке ответа X-Correlation-Id и в логах по всему запросу для связи логов и трейсов.

12) Окружения: Development и Production

Аспект Production (appsettings.json) Development (appsettings.Development.json)
Адреса бэкендов Docker-имена: identity-service:8080, … localhost:5010 (Identity), 5020 (Catalog), 5030 (Booking), 5040 (Chat), 5050 (Media)
CORS Origins https://travelagency.com + http://localhost:3000, http://localhost:5173
HealthCheckEndpoints URL сервисов в Docker-сети URL localhost с соответствующими портами
Уровень логов Serilog Information (по умолчанию) Debug; для Yarp — Debug

13) Запуск и развёртывание

  • Локально: запуск проекта TravelAgency.Gateway (например, из IDE или dotnet run в каталоге проекта). Для работы маршрутов бэкенд-сервисы должны быть доступны по адресам из appsettings.Development.json.
  • Docker: образ собирается по Dockerfile в корне проекта Gateway; в Docker Compose сервис gateway обычно публикуется на одном порту (например, 5000 или 8080) и является единственной точкой входа для клиентов.

Проверка после запуска:

  • GET /health/live — 200.
  • GET /health/ready — 200, если все бэкенды из HealthCheckEndpoints доступны; иначе Degraded.
  • Запрос к любому маршруту (например, GET /api/v1/catalog/tours) при поднятых сервисах должен проксироваться на соответствующий бэкенд.

Связанные документы

Clone this wiki locally