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

Что такое middleware и как оно работает в веб-фреймворках?

2.0 Middle🔥 202 комментариев
#DevOps и инфраструктура#Django

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

🛠️
aaaфывфывфвыф9 апр. 2026 г.

Ллддд

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

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

Что такое middleware и как оно работает в веб-фреймворках?

Основная идея

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

Это паттерн декоратора и цепочки ответственности, используемый во всех серьёзных веб-фреймворках:

  • Django
  • Flask
  • FastAPI
  • aiohttp
  • Express.js (JavaScript)

Как это работает: конвейер

Запрос ────────────────────────────────────
   ↓
Middleware 1 (request)  ← Может модифицировать
   ↓
Middleware 2 (request)
   ↓
Middleware 3 (request)
   ↓
[Ваша бизнес-логика / View]
   ↓
Middleware 3 (response)  ← Может модифицировать
   ↓
Middleware 2 (response)
   ↓
Middleware 1 (response)
   ↓
    ───────────────────────────────────────→ Ответ

Важно: middleware выполняются в обратном порядке при обработке ответа!

Django: Middleware

В Django это классы с методами __init__, process_request, process_response:

class LoggingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # ДО обработки запроса
        print(f"Входящий запрос: {request.method} {request.path}")
        
        # Обработка запроса (вызов остального конвейера)
        response = self.get_response(request)
        
        # ПОСЛЕ обработки запроса
        print(f"Ответ: {response.status_code}")
        
        return response

# В settings.py
MIDDLEWARE = [
    'myapp.middleware.LoggingMiddleware',
    # ... другой middleware
]

FastAPI: Middleware (современный подход)

FastAPI использует асинхронный middleware:

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import time

app = FastAPI()

# Вариант 1: Декоратор
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    # ДО обработки
    start_time = time.time()
    
    # Обработка
    response = await call_next(request)
    
    # ПОСЛЕ обработки
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    
    return response


# Вариант 2: Класс
from starlette.middleware.base import BaseHTTPMiddleware

class CustomMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # ДО
        print(f"Запрос: {request.method} {request.url.path}")
        
        # Обработка
        response = await call_next(request)
        
        # ПОСЛЕ
        response.headers["X-Custom"] = "modified"
        return response

app.add_middleware(CustomMiddleware)

Flask: Middleware через декораторы

Фласк использует более простой подход с before_request и after_request:

from flask import Flask, request, g
import time

app = Flask(__name__)

# Выполняется ДО каждого запроса
@app.before_request
def before_request():
    g.start_time = time.time()
    print(f"Запрос начался: {request.method} {request.path}")

# Выполняется ПОСЛЕ каждого запроса (если не было ошибки)
@app.after_request
def after_request(response):
    elapsed = time.time() - g.start_time
    response.headers["X-Process-Time"] = str(elapsed)
    return response

# Выполняется на ошибках
@app.teardown_request
def teardown_request(exception=None):
    if exception:
        print(f"Ошибка: {exception}")

Примеры полезного middleware

1. Аутентификация

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse

app = FastAPI()

@app.middleware("http")
async def auth_middleware(request: Request, call_next):
    # Проверяем наличие токена
    token = request.headers.get("Authorization")
    
    if not token and "/auth/" not in request.url.path:
        return JSONResponse(
            status_code=401,
            content={"detail": "Требуется авторизация"}
        )
    
    # Сохраняем пользователя в request
    if token:
        request.state.user = parse_token(token)
    
    # Продолжаем обработку
    response = await call_next(request)
    return response

2. Логирование

import logging
from fastapi import Request

logger = logging.getLogger(__name__)

@app.middleware("http")
async def logging_middleware(request: Request, call_next):
    # Логируем входящий запрос
    logger.info(f"{request.method} {request.url.path}")
    
    response = await call_next(request)
    
    # Логируем ответ
    logger.info(f"Status: {response.status_code}")
    
    return response

3. CORS (Cross-Origin Resource Sharing)

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_methods=["GET", "POST"],
    allow_headers=["Content-Type"],
)

4. Проверка прав доступа

from fastapi import Request, HTTPException

@app.middleware("http")
async def admin_middleware(request: Request, call_next):
    # Проверяем доступ к /admin
    if request.url.path.startswith("/admin/"):
        user = request.state.user
        
        if not user or not user.is_admin:
            return JSONResponse(
                status_code=403,
                content={"detail": "Доступ запрещён"}
            )
    
    return await call_next(request)

5. Кэширование

from fastapi import Request
from fastapi.responses import Response

cache = {}

@app.middleware("http")
async def cache_middleware(request: Request, call_next):
    # Кэшируем только GET запросы
    cache_key = f"{request.method}:{request.url.path}"
    
    if request.method == "GET" and cache_key in cache:
        return cache[cache_key]
    
    response = await call_next(request)
    
    if request.method == "GET":
        cache[cache_key] = response
    
    return response

Важный момент: порядок выполнения

@app.middleware("http")
async def middleware_1(request: Request, call_next):
    print("M1: до")
    response = await call_next(request)
    print("M1: после")
    return response

@app.middleware("http")
async def middleware_2(request: Request, call_next):
    print("M2: до")
    response = await call_next(request)
    print("M2: после")
    return response

# Выполнится:
# M1: до
# M2: до
# [View обработка]
# M2: после
# M1: после

Важно: middleware добавляются в обратном порядке!

Асинхронность в middleware

Современные фреймворки поддерживают асинхронный middleware:

@app.middleware("http")
async def async_db_middleware(request: Request, call_next):
    # Можем использовать await
    async with get_db() as db:
        request.state.db = db
        response = await call_next(request)
    
    return response

Обработка ошибок в middleware

from fastapi import Request
from fastapi.responses import JSONResponse

@app.middleware("http")
async def error_middleware(request: Request, call_next):
    try:
        response = await call_next(request)
    except ValueError as e:
        return JSONResponse(
            status_code=400,
            content={"detail": str(e)}
        )
    except Exception as e:
        return JSONResponse(
            status_code=500,
            content={"detail": "Internal server error"}
        )
    
    return response

Сравнение фреймворков

ФреймворкТипСинтаксисАсинхронность
DjangoКласс__call__Нет (исторически)
FastAPIФункция/Класс@app.middlewareДа
FlaskДекоратор@app.before_requestОграниченная
aiohttpФункция@web.middlewareДа

Лучшие практики

  1. Держите middleware простым

    # Плохо: сложная логика в middleware
    @app.middleware("http")
    async def bad_middleware(request, call_next):
        # 100 строк логики...
    
    # Хорошо: выносим в отдельные функции
    async def authenticate(request):
        pass
    
    @app.middleware("http")
    async def good_middleware(request, call_next):
        await authenticate(request)
        return await call_next(request)
    
  2. Не изменяйте request/response внутри

    # Опасно: может сломать другой middleware
    request.method = "GET"
    
    # Хорошо: используем request.state для данных
    request.state.user = user
    
  3. Обрабатывайте исключения

    @app.middleware("http")
    async def safe_middleware(request, call_next):
        try:
            return await call_next(request)
        except Exception as e:
            logger.exception(e)
            return JSONResponse(status_code=500)
    
  4. Используйте контекстные менеджеры

    @app.middleware("http")
    async def context_middleware(request, call_next):
        async with get_transaction() as txn:
            request.state.transaction = txn
            response = await call_next(request)
        return response
    

Заключение

Middleware — это мощный инструмент для обработки сквозных задач (authentication, logging, error handling, CORS). Понимание порядка выполнения и асинхронности критично для разработки надёжных веб-приложений. Помните: middleware выполняются в цепочке, где каждый может модифицировать запрос или ответ до передачи дальше.

🛠️
aaaфывфывфвыф9 апр. 2026 г.(ред.)

Ддддюююю