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

Как используется middleware в Django?

2.0 Middle🔥 231 комментариев
#Базы данных (NoSQL)#Тестирование

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

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

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

Как используется middleware в Django?

Middleware в Django — это система хуков, которые обрабатывают HTTP запросы и ответы на глобальном уровне. Это мощный механизм для реализации кросс-функциональной логики, которая применяется ко всем или некоторым запросам.

Основная концепция

Middleware работает как цепочка фильтров (middleware chain). Каждый middleware имеет возможность:

  1. process_request — обработать запрос ДО view
  2. process_view — обработать ПЕРЕД вызовом view
  3. process_response — обработать ПОСЛЕ view
  4. process_exception — обработать исключения

Порядок обработки запроса

HTTP запрос
    ↓
Middleware 1: process_request
    ↓
Middleware 2: process_request
    ↓
Middleware 1: process_view
    ↓
Middleware 2: process_view
    ↓
View
    ↓
Middleware 2: process_response
    ↓
Middleware 1: process_response
    ↓
HTTP ответ

1. Создание простого middleware

# middleware.py
from django.utils.deprecation import MiddlewareMixin
import logging

logger = logging.getLogger(__name__)

class LoggingMiddleware(MiddlewareMixin):
    """Логирование всех запросов"""
    
    def process_request(self, request):
        """Обработать входящий запрос"""
        logger.info(f"Новый запрос: {request.method} {request.path}")
        # request.start_time = time.time()  # Для измерения времени
        return None  # Продолжить обработку
    
    def process_view(self, request, view_func, view_args, view_kwargs):
        """Обработать перед вызовом view функции"""
        logger.info(f"Вызываю view: {view_func.__name__}")
        return None  # None означает продолжить обработку
    
    def process_response(self, request, response):
        """Обработать ответ"""
        logger.info(f"Ответ: {response.status_code}")
        return response  # ВСЕГДА возвращаем response
    
    def process_exception(self, request, exception):
        """Обработать исключение"""
        logger.error(f"Ошибка: {exception}", exc_info=True)
        return None  # None передаёт исключение дальше

# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'myapp.middleware.LoggingMiddleware',  # Наш middleware
    'django.middleware.common.CommonMiddleware',
]

2. Middleware для аутентификации

from django.contrib.auth.models import AnonymousUser
from django.utils.functional import SimpleLazyObject
from functools import wraps
import jwt

class JWTAuthMiddleware(MiddlewareMixin):
    """Аутентификация через JWT токен"""
    
    def process_request(self, request):
        # Получаем токен из заголовка
        auth_header = request.META.get('HTTP_AUTHORIZATION', '')
        
        if not auth_header.startswith('Bearer '):
            request.user = AnonymousUser()
            return None
        
        token = auth_header[7:]  # Убираем "Bearer "
        
        try:
            # Декодируем JWT токен
            payload = jwt.decode(token, 'secret-key', algorithms=['HS256'])
            user_id = payload.get('user_id')
            
            # Получаем пользователя
            from django.contrib.auth.models import User
            request.user = User.objects.get(id=user_id)
        except (jwt.InvalidTokenError, User.DoesNotExist):
            request.user = AnonymousUser()
        
        return None

3. Middleware для CORS

class CORSMiddleware(MiddlewareMixin):
    """Разрешить кросс-доменные запросы"""
    
    ALLOWED_ORIGINS = [
        'http://localhost:3000',
        'https://example.com',
    ]
    
    def process_request(self, request):
        # Обработка preflight запросов
        if request.method == 'OPTIONS':
            return self.get_cors_response(request)
        return None
    
    def process_response(self, request, response):
        origin = request.META.get('HTTP_ORIGIN')
        
        if origin in self.ALLOWED_ORIGINS:
            response['Access-Control-Allow-Origin'] = origin
            response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
            response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
            response['Access-Control-Allow-Credentials'] = 'true'
        
        return response
    
    def get_cors_response(self, request):
        from django.http import HttpResponse
        response = HttpResponse()
        response['Access-Control-Allow-Origin'] = request.META.get('HTTP_ORIGIN', '')
        response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
        response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
        return response

4. Middleware для кэширования

from django.core.cache import cache
import hashlib

class CacheMiddleware(MiddlewareMixin):
    """Кэширование GET запросов"""
    
    CACHE_TIMEOUT = 60 * 5  # 5 минут
    
    def process_request(self, request):
        # Кэшируем только GET запросы
        if request.method != 'GET':
            return None
        
        # Генерируем ключ кэша
        cache_key = self._get_cache_key(request)
        
        # Проверяем кэш
        cached_response = cache.get(cache_key)
        if cached_response:
            print(f"Вернули из кэша: {request.path}")
            return cached_response
        
        # Сохраняем ключ в request для process_response
        request.cache_key = cache_key
        return None
    
    def process_response(self, request, response):
        # Кэшируем успешные ответы
        if hasattr(request, 'cache_key') and response.status_code == 200:
            cache.set(request.cache_key, response, self.CACHE_TIMEOUT)
        
        return response
    
    @staticmethod
    def _get_cache_key(request):
        """Генерировать уникальный ключ кэша"""
        key_string = f"{request.path}?{request.GET.urlencode()}"
        return f"cache:{hashlib.md5(key_string.encode()).hexdigest()}"

5. Middleware для мониторинга производительности

import time
from django.core.mail import mail_admins

class PerformanceMiddleware(MiddlewareMixin):
    """Отслеживание медленных запросов"""
    
    SLOW_REQUEST_THRESHOLD = 1.0  # 1 секунда
    
    def process_request(self, request):
        request.start_time = time.time()
        return None
    
    def process_response(self, request, response):
        if not hasattr(request, 'start_time'):
            return response
        
        duration = time.time() - request.start_time
        
        # Логируем медленные запросы
        if duration > self.SLOW_REQUEST_THRESHOLD:
            message = (
                f"Медленный запрос!\n"
                f"URL: {request.path}\n"
                f"Метод: {request.method}\n"
                f"Время: {duration:.2f}с\n"
                f"Статус: {response.status_code}"
            )
            mail_admins("Медленный запрос", message)
        
        # Добавляем заголовок с временем обработки
        response['X-Process-Time'] = f"{duration:.2f}s"
        return response

6. Middleware для проверки прав доступа

from django.http import JsonResponse
from django.urls import resolve

class PermissionMiddleware(MiddlewareMixin):
    """Проверка прав доступа до view"""
    
    # Эндпоинты, требующие аутентификации
    PROTECTED_PATHS = [
        '/api/admin/',
        '/api/user/profile/',
    ]
    
    # Публичные эндпоинты
    PUBLIC_PATHS = [
        '/api/auth/login/',
        '/api/auth/register/',
        '/health/',
    ]
    
    def process_view(self, request, view_func, view_args, view_kwargs):
        # Проверяем публичные пути
        if any(request.path.startswith(p) for p in self.PUBLIC_PATHS):
            return None
        
        # Проверяем защищённые пути
        if any(request.path.startswith(p) for p in self.PROTECTED_PATHS):
            if not request.user.is_authenticated:
                return JsonResponse(
                    {'error': 'Требуется аутентификация'},
                    status=401
                )
            
            # Проверяем admin права
            if '/admin/' in request.path and not request.user.is_staff:
                return JsonResponse(
                    {'error': 'Недостаточно прав'},
                    status=403
                )
        
        return None

7. Middleware для обработки ошибок

from django.http import JsonResponse
import traceback

class ErrorHandlingMiddleware(MiddlewareMixin):
    """Обработка необработанных ошибок"""
    
    def process_exception(self, request, exception):
        # Логируем исключение
        logger.error(
            f"Необработанная ошибка: {exception}",
            exc_info=True,
            extra={'request': request}
        )
        
        # Отправляем управляемый ответ
        if request.path.startswith('/api/'):
            return JsonResponse(
                {
                    'error': 'Внутренняя ошибка сервера',
                    'type': exception.__class__.__name__,
                },
                status=500
            )
        
        # Для обычных страниц оставляем обработку Django
        return None

8. Middleware для трассировки запросов

import uuid
from contextvars import ContextVar

request_id = ContextVar('request_id', default=None)

class RequestIDMiddleware(MiddlewareMixin):
    """Добавить уникальный ID для каждого запроса"""
    
    def process_request(self, request):
        # Генерируем или получаем ID
        trace_id = str(uuid.uuid4())
        request.trace_id = trace_id
        request_id.set(trace_id)
        
        return None
    
    def process_response(self, request, response):
        if hasattr(request, 'trace_id'):
            response['X-Trace-ID'] = request.trace_id
        return response

# Использование в логировании
import logging

class TraceIDFilter(logging.Filter):
    def filter(self, record):
        record.trace_id = request_id.get() or 'unknown'
        return True

logger = logging.getLogger(__name__)
logger.addFilter(TraceIDFilter())

Порядок встроенных middleware в Django

# settings.py (по умолчанию)
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',           # HTTPS redirect
    'django.contrib.sessions.middleware.SessionMiddleware',     # Sessions
    'django.middleware.common.CommonMiddleware',               # URL normalization
    'django.middleware.csrf.CsrfViewMiddleware',               # CSRF protection
    'django.contrib.auth.middleware.AuthenticationMiddleware', # Authentication
    'django.contrib.messages.middleware.MessageMiddleware',    # Messages
    'django.middleware.clickjacking.XFrameOptionsMiddleware', # Clickjacking
]

Важные правила

  1. process_request может вернуть HttpResponse (прерывает цепь)
  2. process_response ДОЛЖЕН вернуть HttpResponse
  3. process_exception может вернуть HttpResponse
  4. Middleware выполняются в порядке settings.MIDDLEWARE
  5. Response middleware выполняются в ОБРАТНОМ порядке

Пример сложного middleware

class ComplexMiddleware(MiddlewareMixin):
    """Комплексный middleware с несколькими функциями"""
    
    def process_request(self, request):
        # 1. Добавляем трассировку
        request.request_id = str(uuid.uuid4())
        request.start_time = time.time()
        
        # 2. Проверяем率限
        if self._is_rate_limited(request):
            return JsonResponse({'error': 'Rate limit exceeded'}, status=429)
        
        return None
    
    def process_view(self, request, view_func, view_args, view_kwargs):
        # Проверяем требования к аутентификации
        if hasattr(view_func, 'require_auth') and not request.user.is_authenticated:
            return JsonResponse({'error': 'Unauthorized'}, status=401)
        
        return None
    
    def process_response(self, request, response):
        # Добавляем заголовки
        response['X-Request-ID'] = request.request_id
        response['X-Process-Time'] = str(time.time() - request.start_time)
        
        return response
    
    def _is_rate_limited(self, request):
        # Логика rate limiting
        return False

Выводы

Middleware в Django используется для:

  • Аутентификации и авторизации (JWT, session)
  • Логирования и мониторинга (производительность, ошибки)
  • Кэширования (GET запросы)
  • CORS и безопасности (заголовки, CSRF)
  • Обработки ошибок (глобальный error handler)
  • Трассировки (request ID, context)

Это мощный инструмент для реализации кросс-функциональной логики без дублирования кода во всех view функциях.