Как в FastAPI организовать маршруты для разных субдоменов?

Как определить, на какой суб-домен поступил запрос к бекенду, учитывая, что у нас есть два суб-домена: <b>private.</b> (для внутреннего использования) и <b>public.</b> (для публичного API)? Оба суб-домена могут иметь одинаковые эндпоинты, например, /users/{user_id}, и важно корректно обрабатывать запросы и формировать ответы в зависимости от их источника. Используется ли для этого <b>APIRouter</b> или <b>мидлвары</b>? Интересны мнения и опыт тех, кто реализовывал подобные решения в своих проектах. Заранее благодарю за ответы!
  • 14 ноября 2024 г. 12:23
Ответы на вопрос 2
В FastAPI можно организовать маршруты для разных субдоменов с помощью `APIRouter` и мидлваров для обработки запросов на уровне приложения. Давайте подробнее разберем, как это можно сделать.

### Шаг 1: Определение субдомена

Сначала необходимо определять, на какой субдомен поступил запрос. Это можно сделать с использованием класса `Request`, который предоставляет FastAPI. Для этого мы можем создать специальную мидлвару:

```python
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware

class SubdomainMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        subdomain = request.url.hostname.split('.')[0]  # Получаем субдомен из хоста
        request.state.subdomain = subdomain  # Сохраняем субдомен в состоянии запроса
        response = await call_next(request)
        return response

app = FastAPI()

# Добавляем нашу мидлвару
app.add_middleware(SubdomainMiddleware)
```

### Шаг 2: Создание маршрутов с использованием APIRouter

Теперь мы можем создать разные маршрутизаторы для разных субдоменов и использовать их в зависимости от значения `request.state.subdomain`.

```python
from fastapi import APIRouter, Depends

private_router = APIRouter()
public_router = APIRouter()

@private_router.get("/users/{user_id}")
async def get_private_user(user_id: int, request: Request):
    # Логика для приватного API
    return {"subdomain": request.state.subdomain, "user_id": user_id}

@public_router.get("/users/{user_id}")
async def get_public_user(user_id: int, request: Request):
    # Логика для публичного API
    return {"subdomain": request.state.subdomain, "user_id": user_id}

@app.middleware("http")
async def subdomain_router(request: Request, call_next):
    if request.state.subdomain == "private":
        response = await private_router.routes[0].endpoint(request, user_id=int(request.path_params['user_id']))
    elif request.state.subdomain == "public":
        response = await public_router.routes[0].endpoint(request, user_id=int(request.path_params['user_id']))
    else:
        response = JSONResponse(content={"error": "Subdomain not found"}, status_code=404)
    return response

app.include_router(private_router, prefix="/private")
app.include_router(public_router, prefix="/public")
```

### Шаг 3: Пример использования

Таким образом, запросы к `private.example.com/users/1` и `public.example.com/users/1` будут правильно обрабатываться благодаря делегированию маршрутов в зависимости от субдомена.

### Важно

1. **Настройка DNS**: Убедитесь, что ваши субдомены правильно настроены в DNS.
2. **Серверное конфигурирование**: Проверьте настройки вашего сервера (например, Nginx или Apache) так, чтобы запросы к субдомена корректно перенаправлялись на приложение FastAPI.

### Заключение

Использование мидлваров позволяет вам легко определять субдомен и делать маршрутизацию в зависимости от него. Такой подход позволяет избежать конфликтов в эндпоинтах и поддерживать чистую архитектуру приложения.
Вообще разделить, создаешь два разных экземпляра FastAPI (в разных файлов), для каждого описываешь только нужные эндпоинты, и даже запускаешь их как разные сервера.
Похожие вопросы