← Назад к вопросам

Какие объекты обрабатывает middleware?

2.0 Middle🔥 151 комментариев
#FastAPI и Flask

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Какие объекты обрабатывает middleware

Middleware — это промежуточный обработчик, который работает между клиентом и приложением. Он перехватывает и трансформирует различные объекты на разных этапах жизненного цикла запроса.

Основные объекты в HTTP middleware

1. Request объект

Объект HTTP запроса от клиента.

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

app = FastAPI()

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Middleware получает Request объект
        print(f"Method: {request.method}")
        print(f"URL: {request.url}")
        print(f"Headers: {request.headers}")
        print(f"Query params: {request.query_params}")
        
        # Можно читать body (осторожно! иногда stream-based)
        body = await request.body()
        print(f"Body: {body}")
        
        # Передаём дальше
        response = await call_next(request)
        return response

Атрибуты Request:

  • method — GET, POST, PUT, DELETE и т.п.
  • url — полный URL
  • path — путь без базового URL
  • query_params — параметры строки запроса
  • headers — HTTP заголовки
  • body() — тело запроса (асинхронно)
  • json() — парсированный JSON
  • form() — данные формы
  • cookies — cookies
  • client — IP адрес и порт клиента
  • user — аутентифицированный пользователь (если есть)
  • scope — ASGI scope dict

2. Response объект

Объект HTTP ответа, который возвращает обработчик.

from starlette.responses import Response, JSONResponse

class ResponseModifyMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        response: Response = await call_next(request)
        
        # Middleware может модифицировать Response
        print(f"Status code: {response.status_code}")
        print(f"Headers: {response.headers}")
        
        # Добавить заголовок
        response.headers["X-Custom-Header"] = "middleware-added"
        
        # Изменить status code
        if response.status_code == 404:
            response.status_code = 200
        
        return response

Атрибуты Response:

  • status_code — HTTP код (200, 404, 500)
  • headers — ответные заголовки
  • body — тело ответа (bytes)
  • media_type — MIME тип

3. Cookies

Cookies хранятся в Request и Response.

class CookieMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Читать cookies из запроса
        session_id = request.cookies.get("session_id")
        print(f"Session: {session_id}")
        
        response = await call_next(request)
        
        # Установить cookie в ответ
        response.set_cookie(
            key="tracking",
            value="tracked",
            max_age=3600,
            httponly=True  # Важно для безопасности!
        )
        
        return response

4. Headers

HTTP заголовки в запросе и ответе.

class SecurityHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Прочитать заголовки запроса
        auth_token = request.headers.get("Authorization")
        user_agent = request.headers.get("User-Agent")
        
        response = await call_next(request)
        
        # Добавить заголовки безопасности в ответ
        response.headers["X-Content-Type-Options"] = "nosniff"
        response.headers["X-Frame-Options"] = "DENY"
        response.headers["Strict-Transport-Security"] = "max-age=31536000"
        response.headers["Content-Security-Policy"] = "default-src 'self'"
        
        return response

5. ASGI Scope и Receive/Send

Низкоуровневые ASGI объекты (для специальных случаев).

from starlette.types import ASGIApp, Message, Receive, Send, Scope

class ASGIMiddleware:
    def __init__(self, app: ASGIApp):
        self.app = app
    
    async def __call__(self, scope: Scope, receive: Receive, send: Send):
        # scope — информация о подключении
        print(f"Type: {scope['type']}")  # 'http' или 'websocket'
        print(f"Method: {scope['method']}")
        print(f"Path: {scope['path']}")
        print(f"Headers: {scope['headers']}")
        
        # receive — асинхронный канал получения сообщений
        # send — асинхронный канал отправки сообщений
        
        await self.app(scope, receive, send)

ASGI цикл:

clиент → receive() → middleware ← send() → клиент

6. Stream (для больших тел запроса)

Для файлов и потоков данных.

class UploadMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Для файловых upload-ов body обычно stream
        if request.method == "POST" and "multipart/form-data" in request.headers.get("content-type", ""):
            # Можно читать body частями
            async for chunk in request.stream():
                print(f"Chunk size: {len(chunk)}")
        
        response = await call_next(request)
        return response

7. State (для передачи данных между middleware)

Объект для хранения state между middleware и обработчиком.

from fastapi import Request, FastAPI
import time

app = FastAPI()

class PerformanceMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Сохранить start time в state
        request.state.start_time = time.time()
        
        response = await call_next(request)
        
        # Получить данные из state
        duration = time.time() - request.state.start_time
        response.headers["X-Process-Time"] = str(duration)
        
        return response

@app.get("/")
def read_root(request: Request):
    # Обработчик может читать state из middleware
    start_time = request.state.start_time
    return {"message": "Hello", "start_time": start_time}

Типы middleware в разных фреймворках

FastAPI / Starlette

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware.gzip import GZIPMiddleware

app = FastAPI()

# Встроенный middleware для CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# GZIP compression
app.add_middleware(GZIPMiddleware, minimum_size=1000)

# Custom middleware
class CustomMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Обрабатываю Request
        response = await call_next(request)
        # Обрабатываю Response
        return response

app.add_middleware(CustomMiddleware)

Django

# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'myapp.middleware.CustomMiddleware',
]

# myapp/middleware.py
class CustomMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):  # Request перед обработчиком
        # Обработка request
        print(f"Request: {request.method} {request.path}")
        
        response = self.get_response(request)  # Вызов обработчика
        
        # Обработка response
        response['X-Custom-Header'] = 'value'
        return response

Flask

from flask import Flask, request, after_this_request

app = Flask(__name__)

@app.before_request
def before_request():
    # Обработка перед обработчиком
    print(f"Request: {request.method} {request.path}")
    request.custom_data = {"start_time": time.time()}

@app.after_request
def after_request(response):
    # Обработка после обработчика
    response.headers['X-Custom'] = 'value'
    return response

Celery / Message Queue middleware

from celery import Celery
from celery.signals import before_task_publish, task_prerun, task_postrun

app = Celery()

@before_task_publish.connect
def before_publish(sender=None, body=None, **kwargs):
    # Middleware перед отправкой задачи
    print(f"Publishing task: {sender}")

@task_prerun.connect
def task_prerun_handler(sender=None, task_id=None, **kwargs):
    # Middleware перед запуском задачи
    print(f"Task {task_id} starting")

@task_postrun.connect
def task_postrun_handler(sender=None, task_id=None, **kwargs):
    # Middleware после завершения задачи
    print(f"Task {task_id} completed")

Практический пример: Auth middleware

from fastapi import FastAPI, Request, HTTPException, status
from starlette.middleware.base import BaseHTTPMiddleware
import jwt

app = FastAPI()

class AuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Пропускаем публичные endpoints
        public_paths = {"/health", "/docs", "/login"}
        if request.url.path in public_paths:
            return await call_next(request)
        
        # Получить токен из заголовка
        auth_header = request.headers.get("Authorization")
        if not auth_header:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
        
        try:
            # Парсить и проверить токен
            scheme, token = auth_header.split()
            if scheme.lower() != "bearer":
                raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
            
            payload = jwt.decode(token, "secret-key", algorithms=["HS256"])
            user_id = payload.get("user_id")
            
            # Сохранить пользователя в state
            request.state.user_id = user_id
        except (jwt.InvalidTokenError, ValueError):
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
        
        response = await call_next(request)
        return response

app.add_middleware(AuthMiddleware)

@app.get("/profile")
def get_profile(request: Request):
    user_id = request.state.user_id  # Получено из middleware
    return {"user_id": user_id, "name": "John"}

Сравнение объектов

ОбъектТипИспользуется дляПример
RequestHTTPЧтение данных клиентаrequest.method, request.body()
ResponseHTTPИзменение ответаresponse.status_code, response.headers
HeadersHTTPSecurity, CORS, CachingX-Custom, Authorization
CookiesHTTPSession, Trackingsession_id, csrf_token
ASGI ScopeASGIНизкоуровневая обработкаscope['type'], scope['method']
StreamDataБольшие файлыrequest.stream()
StateContextПередача между middlewarerequest.state.user_id
Message (Task)QueueФоновые задачиCelery, RabbitMQ

Порядок выполнения middleware

request
  ↓
[Middleware 1] → request phase
  ↓
[Middleware 2] → request phase
  ↓
[Handler]
  ↓
[Middleware 2] → response phase
  ↓
[Middleware 1] → response phase
  ↓
response

Порядок добавления важен! Первый добавленный middleware = последний в цепи.

app.add_middleware(AuthMiddleware)       # Выполнится 2-й
app.add_middleware(LoggingMiddleware)    # Выполнится 1-й

Best practices для middleware

class GoodMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # ✅ DO: Оберни в try-except
        try:
            response = await call_next(request)
        except Exception as e:
            # Логируй ошибку
            logger.exception(f"Error in handler: {e}")
            # Верни ошибку
            return JSONResponse(
                status_code=500,
                content={"error": "Internal Server Error"}
            )
        
        # ✅ DO: Не блокируй (async)
        response.headers["X-Process-Time"] = str(time.time())
        
        # ✅ DO: Не читай body без необходимости
        # (body может быть большой, и сумеш прочитать только раз)
        
        return response

class BadMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # ❌ DON'T: Читаю body, и потом обработчик не может его прочитать
        body = await request.body()
        # body stream уже исчерпан!
        
        # ❌ DON'T: Синхронный код (блокирует)
        time.sleep(1)  # Медленно!
        
        # ❌ DON'T: Не ловлю ошибки
        response = await call_next(request)
        
        return response

Заключение

Middleware обрабатывает:

  1. Request — метод, URL, заголовки, body, cookies
  2. Response — status code, headers, body
  3. State — контекст для передачи данных
  4. ASGI-level objects — для специальных случаев

Мидлвэйр — это мощный инструмент для cross-cutting concerns: аутентификация, логирование, кэширование, rate limiting и т.п.

В production я всегда использую middleware для:

  • Authentication & Authorization
  • Logging & Monitoring
  • Error Handling
  • CORS & Security Headers
  • Request/Response transformation
Какие объекты обрабатывает middleware? | PrepBro