Estructura de un middleware
Un middleware moderno en Django es una clase callable que envuelve la vista:
# middlewares.py
import time
import logging
logger = logging.getLogger(__name__)
class MiddlewareBase:
def __init__(self, get_response):
"""
Se ejecuta una sola vez al inicializar el servidor.
Ideal para configuraciones costosas.
"""
self.get_response = get_response
# Configuración inicial aquí
def __call__(self, request):
"""Se ejecuta en cada petición."""
# Código antes de la vista
respuesta = self.get_response(request)
# Código después de la vista
return respuesta

Middleware de logging de peticiones
class LogPeticionesMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.logger = logging.getLogger('peticiones')
def __call__(self, request):
inicio = time.time()
respuesta = self.get_response(request)
duracion = (time.time() - inicio) * 1000 # ms
self.logger.info(
'%s %s %s %dms %s',
request.method,
request.path,
respuesta.status_code,
duracion,
request.user.username if request.user.is_authenticated else 'anónimo'
)
# Añadir cabecera de tiempo de respuesta
respuesta['X-Response-Time'] = f'{duracion:.2f}ms'
return respuesta
Middleware de limitación de tasa
from django.core.cache import cache
from django.http import JsonResponse
class LimitarTasaMiddleware:
"""Limita el número de peticiones por IP."""
LIMITE_PETICIONES = 100
VENTANA_SEGUNDOS = 60
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
ip = self.obtener_ip(request)
clave = f'rate_limit:{ip}'
contador = cache.get(clave, 0)
if contador >= self.LIMITE_PETICIONES:
return JsonResponse(
{'error': 'Demasiadas peticiones. Intenta de nuevo en 1 minuto.'},
status=429
)
# Incrementar contador
if contador == 0:
cache.set(clave, 1, self.VENTANA_SEGUNDOS)
else:
cache.incr(clave)
respuesta = self.get_response(request)
respuesta['X-RateLimit-Remaining'] = self.LIMITE_PETICIONES - contador - 1
return respuesta
def obtener_ip(self, request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
return x_forwarded_for.split(',')[0].strip()
return request.META.get('REMOTE_ADDR', '0.0.0.0')
Middleware con process_exception
Para manejo de excepciones, implementa el método process_exception:
import traceback
class ManejoExcepcionesMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.logger = logging.getLogger('excepciones')
def __call__(self, request):
return self.get_response(request)
def process_exception(self, request, exception):
"""
Se llama cuando una vista lanza una excepción no capturada.
Devolver None permite que Django maneje la excepción normalmente.
Devolver HttpResponse intercepta la excepción.
"""
self.logger.error(
'Excepción en %s %s: %s\n%s',
request.method,
request.path,
str(exception),
traceback.format_exc()
)
if isinstance(exception, PermissionError):
from django.http import JsonResponse
return JsonResponse({'error': 'Sin permiso'}, status=403)
return None # Django maneja el resto
Middleware de cabeceras de seguridad
class CabecerasSeguridad:
"""Añade cabeceras de seguridad HTTP a todas las respuestas."""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
respuesta = self.get_response(request)
respuesta['X-Content-Type-Options'] = 'nosniff'
respuesta['X-Frame-Options'] = 'DENY'
respuesta['Referrer-Policy'] = 'strict-origin-when-cross-origin'
respuesta['Permissions-Policy'] = 'camera=(), microphone=(), geolocation=()'
if not respuesta.get('Cache-Control'):
respuesta['Cache-Control'] = 'no-cache, no-store, must-revalidate'
return respuesta
Registrar middleware
El orden en MIDDLEWARE es fundamental. Los middleware se ejecutan de arriba a abajo en la petición y de abajo a arriba en la respuesta:
# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'miapp.middlewares.LimitarTasaMiddleware', # Antes de procesar la sesión
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'miapp.middlewares.LogPeticionesMiddleware', # Después de autenticación
'miapp.middlewares.CabecerasSeguridad',
]
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Django es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Django
Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Entender el ciclo de vida de las peticiones a través del stack de middleware. Crear un middleware personalizado con la estructura init y call. Interceptar peticiones con lógica antes de llegar a la vista. Procesar respuestas antes de enviarlas al cliente. Manejar excepciones con process_exception en el middleware.