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

Разработанный с учетом высокой гибкости, FastAPI принимает любое промежуточное ПО, соответствующее спецификации ASGI. Это означает, что вы можете интегрировать в FastAPI-приложение как встроенные промежуточные программы, предоставляемые FastAPI или Starlette, так и промежуточное ПО от сторонних производителей, а также создавать собственное промежуточное ПО для удовлетворения конкретных потребностей. Программным обеспечением ASGI может быть любой вызываемый модуль, соответствующий спецификации ASGI, что позволяет беспроблемно внедрять в FastAPI-приложение широкий спектр функциональных возможностей.

Вот перечень встроенных (FastAPI и/или Starlette) и сторонних пользовательских промежуточных программ, которые могут быть полезны при разработке приложений. 

1. CORSMiddleware  

Описание 

CORS расшифровывается как «cross-origin resource sharing» — совместное использование ресурсов из разных источников. CORS позволяет запрашивать или ограничивать ресурсы на сервере из другого домена. Это необходимо, когда бэкенд FastAPI обслуживает клиентов различного происхождения, например фронтенд-приложения, размещенные на разных доменах. 

Случаи использования

  • Обеспечение безопасного использования междоменных запросов в одностраничных приложениях.
  • Предоставление доступа к API из разных клиентских доменов.

Пример

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Allows all origins
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
async def root():
    return {"message": "Hello World"}

2. GZipMiddleware  

Описание

Автоматически сжимает ответы с помощью алгоритма GZip, уменьшая размер полезной нагрузки и повышая производительность, особенно для больших ответов JSON или HTML.

Случаи использования

  • Обслуживание больших JSON-ответов в RESTful API.
  • Обслуживание статических файлов со сжатым контентом.

Пример

from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware

app = FastAPI()
app.add_middleware(GZipMiddleware, minimum_size=1000) # Сжатие ответов размером более 1000 байт

@app.get("/")
async def root():
return {"message": "This is a test message that will be compressed."}

3. HTTPSRedirectMiddleware 

Описание

Позволяет автоматически перенаправить все HTTP-запросы на HTTPS, обеспечивая безопасность соединений.

Случаи использования

  • Обеспечение HTTPS для веб-приложений.
  • Защита конечных точек API путем обеспечения кодирования данных в сети.

Пример

from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware

app = FastAPI()
app.add_middleware(HTTPSRedirectMiddleware)

@app.get("/")
async def root():
    return {"message": "You are being redirected to HTTPS!"}

4. SessionMiddleware 

Описание

Управляет сессиями пользователей, создавая идентификатор сессии для каждого пользователя и сохраняя его в куки-файлах, что позволяет легко поддерживать взаимодействие с учетом состояния при нескольких запросах.

Случаи использования

  • Управление сессиями аутентификации пользователей.
  • Хранение временных данных при нескольких запросах.

Пример

from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware

app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="your-secret-key")

@app.get("/set/")
async def set_session_data(request: Request):
    request.session['user'] = 'john_doe'
    return {"message": "Session data set"}

@app.get("/get/")
async def get_session_data(request: Request):
    user = request.session.get('user', 'guest')
    return {"user": user}

5. TrustedHostMiddleware

Описание

Фильтрует запросы, чтобы подтвердить их поступление от определенных доверенных имен хостов. Защищает от атак на заголовки хостов в HTTP-запросах.

Случаи использования

  • Защита от атак на DNS-перепривязку.
  • Ограничение доступа к API определенными именами хостов.

Пример

from fastapi import FastAPI
from fastapi.middleware.trustedhost import TrustedHostMiddleware

app = FastAPI()
app.add_middleware(TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"])

@app.get("/")
async def root():
    return {"message": "This request came from a trusted host."}

6. ErrorHandlingMiddleware 

Описание

Это пользовательское ПО перехватывает исключения и единообразно форматирует ответы на ошибки. Его можно настроить для регистрации ошибок или уведомления служб мониторинга.

Случаи использования

  • Пользовательское регистрирование ошибок.
  • Единообразное форматирование ответов на ошибки во всем приложении.

Пример

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware

class ErrorHandlingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        try:
            response = await call_next(request)
        except Exception as e:
            response = JSONResponse({"error": str(e)}, status_code=500)
        return response

app = FastAPI()
app.add_middleware(ErrorHandlingMiddleware)

@app.get("/")
async def root():
    raise ValueError("This is an error!")

7. RateLimitingMiddleware 

Описание

Ограничивает количество запросов пользователя к API за определенный промежуток времени, помогая предотвратить злоупотребления и угрозу отказа в обслуживании.

Случаи использования

  • Предотвращение злонамеренного использования API путем ограничения частоты запросов.
  • Реализация уровней использования для общедоступных API.

Пример

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
import time

class RateLimitMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, max_requests: int, window: int):
        super().__init__(app)
        self.max_requests = max_requests
        self.window = window
        self.requests = {}

    async def dispatch(self, request: Request, call_next):
        client_ip = request.client.host
        current_time = time.time()

        if client_ip not in self.requests:
            self.requests[client_ip] = []

        self.requests[client_ip] = [timestamp for timestamp in self.requests[client_ip] if timestamp > current_time - self.window]

        if len(self.requests[client_ip]) >= self.max_requests:
            return JSONResponse(status_code=429, content={"error": "Too many requests"})

        self.requests[client_ip].append(current_time)
        return await call_next(request)


app = FastAPI()
app.add_middleware(RateLimitMiddleware, max_requests=5, window=60)

@app.get("/")
async def root():
    return {"message": "You haven't hit the rate limit yet!"}

8. AuthenticationMiddleware

Описание

Обрабатывает аутентификацию, проверяя токены или учетные данные до того, как запросы достигнут конечной точки. Его можно настроить для поддержки JWT, OAuth или других механизмов аутентификации.

Случаи использования

  • Проверка API-запросов с помощью JWT-токенов.
  • Реализация пользовательской логики аутентификации.

Пример

from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import PlainTextResponse

class AuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        token = request.headers.get("Authorization")
        if not token or token != "Bearer valid-token":
            return PlainTextResponse(status_code=401, content="Unauthorized")
        return await call_next(request)

app = FastAPI()
app.add_middleware(AuthMiddleware)

@app.get("/secure-data/")
async def secure_data():
    return {"message": "This is secured data"}

9. HeadersInjectionMiddleware

Описание

Это пользовательское ПО предназначено для добавления в каждый ответ определенных заголовков, которые могут включать пользовательские токены аутентификации, идентификаторы корреляции, заголовки безопасности (security headers) и любые другие пользовательские метаданные.

Случаи использования

  • Добавление пользовательских заголовков, например токенов аутентификации, метаданных, идентификаторов корреляции и т. д.
  • Повышение безопасности за счет установки соответствующих HTTP-заголовков, предотвращающих распространенные веб-уязвимости.
  • Контроль над поведением браузера/клиента при кэшировании путем установки заголовков для управления кэшем.

Пример

from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware

class CustomHeaderMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        response.headers['Cache-Control'] = 'public, max-age=3600'
        response.headers["X-Content-Type-Options"] = "nosniff"
        response.headers["X-Frame-Options"] = "DENY"
        response.headers["X-XSS-Protection"] = "1; mode=block"
        response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
        return response

app = FastAPI()
app.add_middleware(CustomHeaderMiddleware)

@app.get("/data/")
async def get_data():
    return {"message": "This response is cached for 1 hour."}

10. LoggingMiddleware

Описание

Обеспечивает ведение журнала, регистрируя каждый запрос и ответ, что полезно для отладки и мониторинга использования API.

Случаи использования

  • Отладка проблем путем отслеживания циклов запросов/ответов.
  • Мониторинг производительности API.

Пример

from fastapi import FastAPI, Request
import logging
from starlette.middleware.base import BaseHTTPMiddleware

logger = logging.getLogger("my_logger")

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        logger.info(f"Request: {request.method} {request.url}")
        response = await call_next(request)
        logger.info(f"Response status: {response.status_code}")
        return response

app = FastAPI()
app.add_middleware(LoggingMiddleware)

@app.get("/")
async def root():
    return {"message": "Check your logs for the request and response details."}

11. TimeoutMiddleware

Описание

Обеспечивает прерывание запросов, если они занимают больше определенного времени, не позволяя длительным запросам истощать ресурсы сервера.

Случаи использования

  • Предотвращение истощения ресурсов из-за длительных запросов.
  • Обеспечение своевременных ответов в приложениях, требующих высокого трафика.

Пример

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import PlainTextResponse
import asyncio
from starlette.middleware.base import BaseHTTPMiddleware

class TimeoutMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, timeout: int):
        super().__init__(app)
        self.timeout = timeout

    async def dispatch(self, request: Request, call_next):
        try:
            return await asyncio.wait_for(call_next(request), timeout=self.timeout)
        except asyncio.TimeoutError:
            return PlainTextResponse(status_code=504, content="Request timed out")

app = FastAPI()
app.add_middleware(TimeoutMiddleware, timeout=5)

@app.get("/")
async def root():
    await asyncio.sleep(10)  # Simulates a long-running process
    return {"message": "This won't be reached if the timeout is less than 10 seconds."}

12. TrailingSlashMiddleware

Описание

Нормализует URL, добавляя или удаляя концевые косые черты, обеспечивая согласованную маршрутизацию и избегая дублирования маршрутов.

Случаи использования

  • Обеспечение согласованной URL-структуры во всем приложении.
  • Предотвращение проблем с маршрутизацией из-за несогласованного использования URL.

Пример

from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import RedirectResponse


class TrailingSlashMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        if not request.url.path.endswith("/"):
            return RedirectResponse(url=f"{request.url.path}/")
        return await call_next(request)

app = FastAPI()
app.add_middleware(TrailingSlashMiddleware)

@app.get("/hello/")
async def hello():
    return {"message": "Hello with trailing slash!"}

13. IPWhitelistingMiddleware

Описание

Ограничивает доступ к приложению на основе IP-адреса клиента, гарантируя разрешение запросов с определенных IP-адресов.

Случаи использования

  • Ограничение доступа к внутренним API.
  • Добавление дополнительного уровня безопасности путем ограничения доступа по IP-адресу.

Пример

from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import PlainTextResponse

class IPWhitelistMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, whitelist):
        super().__init__(app)
        self.whitelist = whitelist

    async def dispatch(self, request: Request, call_next):
        client_ip = request.client.host
        if client_ip not in self.whitelist:
            return PlainTextResponse(status_code=403, content="IP not allowed")
        return await call_next(request)

app = FastAPI()
app.add_middleware(IPWhitelistMiddleware, whitelist=["127.0.0.1", "192.168.1.1"])

@app.get("/")
async def root():
    return {"message": "Your IP is whitelisted!"}

14. ProxyHeadersMiddleware

Описание

Используется, когда приложение работает через прокси-сервер, например Nginx, или API-шлюз. Считывает заголовки, установленные прокси (например, X-Forwarded-ForX-Forwarded-Proto), чтобы определить исходный IP и схему клиента.

Случаи использования

  • Правильное определение IP-адреса клиента при работе через прокси-сервер.
  • Обработка завершения HTTPS на уровне прокси.

Пример

from fastapi import FastAPI, Request
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware


app = FastAPI()
app.add_middleware(ProxyHeadersMiddleware)


@app.get("/")
async def root(request: Request):
    return {"client_ip": request.client.host}

15. CSRFMiddleware

Описание

Обеспечивает FastAPI-приложению CSRF-защиту (CSRF — cross-site request forgery межсайтовая подделка запросов), генерируя и проверяя CSRF-токены. Это помогает предотвратить выполнение несанкционированных команд от имени аутентифицированного пользователя.

Для использования в FastAPI-приложении промежуточной программы CSRFMiddleware надо установить пакет:

pip install starlette-csrf

Случаи использования

  • Защита форм и действий от CSRF-атак.
  • Обеспечение выполнения только легитимных действий аутентифицированными пользователями.

Пример

from fastapi import FastAPI, Request
from starlette_csrf import CSRFMiddleware

app = FastAPI()

app.add_middleware(CSRFMiddleware, secret="__CHANGE_ME__")


@app.get("/")
async def root(request: Request):
    return {"message": request.cookies.get('csrftoken')}

16. StarletteContextMiddleware

Описание

starlette-context — стороннее промежуточное ПО, используемое в FastAPI (и Starlette) для поддержания и управления контекстом с привязкой к запросу. Оно позволяет хранить и извлекать информацию, специфичную для одного запроса, например заголовки запросов, информацию о пользователе или идентификаторы корреляции, не передавая эти данные через каждую функцию или обработчик маршрута в явном виде.

Это особенно полезно в сложных приложениях, где определенные данные должны быть доступны в течение всего времени выполнения запроса, но необходимо избежать их явной передачи через параметры функции.

Для использования промежуточного ПО starlette-context в FastAPI-приложении надо установить пакет:

pip install starlette-context

Случаи использования

  • Автоматическая фиксация в журналах деталей, относящихся к конкретному запросу (например, идентификатора корреляции).
  • Хранение метаданных, которые относятся к конкретному запросу и должны быть доступны в разных частях приложения.
  • Отслеживание информации о пользователях на разных уровнях приложения (например, в службах или операциях с базой данных).

Пример

from fastapi import FastAPI, Depends
from fastapi.responses import JSONResponse
from starlette_context.middleware import ContextMiddleware
from starlette_context import context


app = FastAPI()
app.add_middleware(ContextMiddleware)


async def set_globals() -> None:
    context["username"] = "chris"


@app.get("/", dependencies=[Depends(set_globals)])
async def info():
    return JSONResponse(context.data)

17. GlobalsMiddleware

GlobalsMiddleware — еще одно стороннее промежуточное ПО, похожее на starlette-context, но ориентированное на обеспечение глобального управления состоянием в ASGI-приложениях подобно тому, как это делает объект g для flask-приложений.

Для использования промежуточного ПО fastapi-g-context в FastAPI-приложении нужно установить пакет:

pip install fastapi-g-context

Случаи использования

  • Хранение настроек всего приложения или состояния, к которому необходимо получить доступ из различных частей приложения.
  • Хранение и получение данных, относящихся к конкретному запросу, без необходимости передавать их через несколько уровней приложения.

Пример

from fastapi import FastAPI, Depends
from fastapi_g_context import GlobalsMiddleware, g

app = FastAPI()
app.add_middleware(GlobalsMiddleware)

async def set_globals() -> None:
    g.username = "JohnDoe"
    g.request_id = "123456"
    g.is_admin = True

@app.get("/", dependencies=[Depends(set_globals)])
async def info():
    return {"username": g.username, "request_id": g.request_id, "is_admin": g.is_admin}

Заключение

Промежуточное ПО — неотъемлемая часть разработки FastAPI-приложений — предлагает множество способов оптимизировать и защитить API. Встроенные промежуточные модули, предоставляемые FastAPI и Starlette, охватывают широкий спектр необходимых функций — от CORS-обработки и управления сессиями до обеспечения безопасных HTTPS-соединений и защиты от CSRF-угроз. Кроме того, возможность интегрировать стороннее промежуточное ПО или создавать свои решения обеспечивает еще большую гибкость, позволяя адаптировать приложение под конкретные нужды.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Chris Karvouniaris: 17 Useful Middlewares for FastAPI that You Should Know About

Предыдущая статьяРоль метода Stream.map() в Java
Следующая статьяУязвимости для SQL-инъекций