Django
Middleware
Buscar..
Introducción
Middleware en Django es un marco que permite que el código se enganche en el procesamiento de respuesta / solicitud y altere la entrada o salida de Django.
Observaciones
Es necesario agregar middleware a su configuración.py MIDDLEWARE_CLASSES
lista antes de que se incluya en la ejecución. La lista predeterminada que proporciona Django al crear un nuevo proyecto es la siguiente:
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Estas son todas las funciones que se ejecutarán en orden en cada solicitud (una vez antes de que alcance su código de vista en views.py
y una vez en orden inverso para devolución de llamada process_response
, antes de la versión 1.10). Hacen una variedad de cosas, como inyectar el token de falsificación de solicitudes en sitios cruzados (csrf) .
El orden importa porque si algún middleware realiza una redirección, el middleware subsiguiente nunca se ejecutará. O si un middleware espera que el token csrf esté allí, tiene que ejecutarse después del CsrfViewMiddleware
.
Añadir datos a las solicitudes
Django hace que sea realmente fácil agregar datos adicionales a las solicitudes de uso dentro de la vista. Por ejemplo, podemos analizar el subdominio en el META de la solicitud y adjuntarlo como una propiedad separada en la solicitud mediante el uso de middleware.
class SubdomainMiddleware:
def process_request(self, request):
"""
Parse out the subdomain from the request
"""
host = request.META.get('HTTP_HOST', '')
host_s = host.replace('www.', '').split('.')
request.subdomain = None
if len(host_s) > 2:
request.subdomain = host_s[0]
Si agrega datos con middleware a su solicitud, puede acceder a los datos recién agregados más adelante en la línea. Aquí usaremos el subdominio analizado para determinar algo como qué organización está accediendo a su aplicación. Este enfoque es útil para las aplicaciones que se implementan con una configuración de DNS con subdominios comodín que apuntan a una sola instancia y la persona que accede a la aplicación desea una versión con apariencia que dependa del punto de acceso.
class OrganizationMiddleware:
def process_request(self, request):
"""
Determine the organization based on the subdomain
"""
try:
request.org = Organization.objects.get(domain=request.subdomain)
except Organization.DoesNotExist:
request.org = None
Recuerde que el orden es importante cuando el middleware depende del otro. Para las solicitudes, deseará que el middleware dependiente se coloque después de la dependencia.
MIDDLEWARE_CLASSES = [
...
'myapp.middleware.SubdomainMiddleware',
'myapp.middleware.OrganizationMiddleware',
...
]
Middleware para filtrar por dirección IP
Primero: La estructura del camino.
Si no lo tiene, debe crear la carpeta de middleware dentro de su aplicación siguiendo la estructura:
yourproject/yourapp/middleware
La carpeta de middleware se debe colocar en la misma carpeta que settings.py, urls, templates ...
Importante: no olvide crear el archivo init .py vacío dentro de la carpeta de middleware para que su aplicación reconozca esta carpeta
En lugar de tener una carpeta separada que contenga sus clases de middleware, también es posible poner sus funciones en un solo archivo, yourproject/yourapp/middleware.py
.
Segundo: Crea el middleware.
Ahora debemos crear un archivo para nuestro middleware personalizado. En este ejemplo, supongamos que queremos un middleware que filtre a los usuarios según su dirección IP, creamos un archivo llamado filter_ip_middleware.py :
#yourproject/yourapp/middleware/filter_ip_middleware.py
from django.core.exceptions import PermissionDenied
class FilterIPMiddleware(object):
# Check if client IP address is allowed
def process_request(self, request):
allowed_ips = ['192.168.1.1', '123.123.123.123', etc...] # Authorized ip's
ip = request.META.get('REMOTE_ADDR') # Get client IP address
if ip not in allowed_ips:
raise PermissionDenied # If user is not allowed raise Error
# If IP address is allowed we don't do anything
return None
Tercero: Agregue el middleware en nuestro 'settings.py'
Necesitamos buscar MIDDLEWARE_CLASSES
dentro de settings.py y allí debemos agregar nuestro middleware (agregarlo en la última posición ). Debería ser como
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Above are Django standard middlewares
# Now we add here our custom middleware
'yourapp.middleware.filter_ip_middleware.FilterIPMiddleware'
)
¡Hecho! Ahora, cada solicitud de cada cliente llamará a su middleware personalizado y procesará su código personalizado.
Excepción de manejo global
Supongamos que ha implementado alguna lógica para detectar intentos de modificar un objeto en la base de datos, mientras que el cliente que envió los cambios no tuvo las últimas modificaciones. Si tal caso ocurre, ConfictError(detailed_message)
una excepción personalizada ConfictError(detailed_message)
.
Ahora desea devolver un código de estado HTTP 409 (Conficto) cuando se produce este error. Por lo general, puede usarlo como middleware para esto en lugar de manejarlo en cada vista que pueda generar esta excepción.
class ConfictErrorHandlingMiddleware:
def process_exception(self, request, exception):
if not isinstance(exception, ConflictError):
return # Propagate other exceptions, we only handle ConflictError
context = dict(confict_details=str(exception))
return TemplateResponse(request, '409.html', context, status=409)
Entendiendo el nuevo estilo del middleware Django 1.10
Django 1.10 introdujo un nuevo estilo de middleware donde se combinan process_request
y process_response
.
En este nuevo estilo, un middleware es un invocable que devuelve otro invocable . Bueno, en realidad la primera es una fábrica de middleware y la última es el middleware real .
La fábrica de middleware toma como único argumento el siguiente middleware en la pila de middlewares, o la vista en sí misma cuando se alcanza la parte inferior de la pila.
El middleware toma la solicitud como único argumento y siempre devuelve un HttpResponse
.
El mejor ejemplo para ilustrar cómo funciona el middleware de nuevo estilo es probablemente mostrar cómo hacer un middleware compatible con versiones anteriores:
class MyMiddleware:
def __init__(self, next_layer=None):
"""We allow next_layer to be None because old-style middlewares
won't accept any argument.
"""
self.get_response = next_layer
def process_request(self, request):
"""Let's handle old-style request processing here, as usual."""
# Do something with request
# Probably return None
# Or return an HttpResponse in some cases
def process_response(self, request, response):
"""Let's handle old-style response processing here, as usual."""
# Do something with response, possibly using request.
return response
def __call__(self, request):
"""Handle new-style middleware here."""
response = self.process_request(request)
if response is None:
# If process_request returned None, we must call the next middleware or
# the view. Note that here, we are sure that self.get_response is not
# None because this method is executed only in new-style middlewares.
response = self.get_response(request)
response = self.process_response(request, response)
return response