Middleware es un marco de enlaces al procesamiento de solicitudes / respuestas de Django. Es un sistema de “complementos” ligero y de bajo nivel para alterar globalmente la entrada o salida de Django.

Cada componente de middleware es responsable de realizar una función específica. Por ejemplo, Django incluye un componente de middleware, AuthenticationMiddleware, que asocia a los usuarios con solicitudes mediante sesiones.

Este documento explica cómo funciona el middleware, cómo se activa el middleware y cómo escribir su propio middleware. Django se envía con un middleware integrado que puede usar desde el primer momento. Están documentados en el referencia de middleware incorporada.

Escribiendo su propio middleware

Una fábrica de middleware es un invocable que requiere get_response invocable y devuelve un middleware. Un middleware es un invocable que toma una solicitud y devuelve una respuesta, como una vista.

Un middleware se puede escribir como una función que tiene este aspecto:

defsimple_middleware(get_response):# One-time configuration and initialization.defmiddleware(request):# Code to be executed for each request before# the view (and later middleware) are called.

        response = get_response(request)# Code to be executed for each request/response after# the view is called.return response

    return middleware

O se puede escribir como una clase cuyas instancias son invocables, así:

classSimpleMiddleware:def__init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.def__call__(self, request):# Code to be executed for each request before# the view (and later middleware) are called.

        response = self.get_response(request)# Code to be executed for each request/response after# the view is called.return response

los get_response invocable proporcionado por Django podría ser la vista real (si este es el último middleware enumerado) o podría ser el siguiente middleware en la cadena. El middleware actual no necesita saber ni preocuparse de qué es exactamente, solo que representa lo que viene a continuación.

Lo anterior es una ligera simplificación: el get_response invocable para el último middleware de la cadena no será la vista real, sino un método contenedor del controlador que se encarga de aplicar ver middleware, llamando a la vista con los argumentos de URL adecuados y aplicando plantilla-respuesta y excepción middleware.

El middleware puede admitir solo Python sincrónico (el predeterminado), solo Python asincrónico o ambos. Ver Soporte asincrónico para obtener detalles sobre cómo publicitar lo que apoya y saber qué tipo de solicitud está recibiendo.

El middleware puede vivir en cualquier lugar de su ruta de Python.

__init__(get_response)

Las fábricas de middleware deben aceptar get_response argumento. También puede inicializar algún estado global para el middleware. Tenga en cuenta un par de advertencias:

  • Django inicializa su middleware con solo el get_response argumento, por lo que no puede definir __init__() como requiriendo cualquier otro argumento.
  • A diferencia del __call__() método que se llama una vez por solicitud, __init__() se llama solo una vez, cuando se inicia el servidor web.

Marcar middleware como no utilizado

A veces es útil determinar en el momento del inicio si se debe utilizar una pieza de middleware. En estos casos, su middleware __init__() el método puede elevar MiddlewareNotUsed. Luego, Django eliminará ese middleware del proceso de middleware y registrará un mensaje de depuración en el django.request registrador cuando DEBUG es True.

Activando middleware

Para activar un componente de middleware, agréguelo al MIDDLEWARE lista en la configuración de Django.

En MIDDLEWARE, cada componente de middleware está representado por un string: la ruta completa de Python a la clase o al nombre de función de la fábrica de middleware. Por ejemplo, aquí está el valor predeterminado creado por django-admin
startproject
:

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','django.middleware.clickjacking.XFrameOptionsMiddleware',]

Una instalación de Django no requiere ningún middleware. MIDDLEWARE puede estar vacío, si lo desea, pero se recomienda encarecidamente que al menos use CommonMiddleware.

El orden en MIDDLEWARE importa porque un middleware puede depender de otro middleware. Por ejemplo, AuthenticationMiddleware almacena al usuario autenticado en la sesión; por lo tanto, debe ejecutarse después SessionMiddleware. Ver Pedido de middleware para obtener algunos consejos comunes sobre cómo ordenar las clases de middleware de Django.

Orden y capas de middleware

Durante la fase de solicitud, antes de llamar a la vista, Django aplica el middleware en el orden en el que está definido. MIDDLEWARE, De arriba hacia abajo.

Puede pensar en ello como una cebolla: cada clase de middleware es una “capa” que envuelve la vista, que está en el núcleo de la cebolla. Si la solicitud pasa por todas las capas de la cebolla (cada una llama get_response para pasar la solicitud a la siguiente capa), hasta la vista en el núcleo, la respuesta luego pasará por cada capa (en orden inverso) en el camino de regreso.

Si una de las capas decide hacer un cortocircuito y devolver una respuesta sin llamar a su get_response, ninguna de las capas de la cebolla dentro de esa capa (incluida la vista) verá la solicitud o la respuesta. La respuesta solo volverá a través de las mismas capas por las que pasó la solicitud.

Otros ganchos de middleware

Además del patrón de middleware de solicitud / respuesta básico descrito anteriormente, puede agregar otros tres métodos especiales al middleware basado en clases:

process_view()

process_view(request, view_func, view_args, view_kwargs)

request es un HttpRequest objeto. view_func es la función de Python que Django está a punto de usar. (Es el objeto de función real, no el nombre de la función como un string.) view_args es una lista de argumentos posicionales que se pasarán a la vista, y view_kwargs es un diccionario de argumentos de palabras clave que se pasarán a la vista. Ninguno view_args ni view_kwargs incluir el primer argumento de vista (request).

process_view() se llama justo antes de que Django llame a la vista.

Debería volver None o un HttpResponse objeto. Si vuelve None, Django continuará procesando esta solicitud, ejecutando cualquier otra process_view() middleware y, a continuación, la vista adecuada. Si devuelve un HttpResponse objeto, Django no se molestará en llamar a la vista apropiada; aplicará middleware de respuesta a eso HttpResponse y devuelve el resultado.

Nota

Accediendo request.POST dentro del middleware antes de que se ejecute la vista o en process_view() evitará que cualquier vista que se ejecute después de que el middleware pueda modificar los controladores de carga para la solicitud, y normalmente debe evitarse.

los CsrfViewMiddleware La clase puede considerarse una excepción, ya que proporciona la csrf_exempt() y csrf_protect() decoradores que permiten que las vistas controlen explícitamente en qué punto debe ocurrir la validación CSRF.

process_exception()

process_exception(request, exception)

request es un HttpRequest objeto. exception es un Exception objeto generado por la función de vista.

Llamadas de Django process_exception() cuando una vista genera una excepción. process_exception() debería volver None o un HttpResponse objeto. Si devuelve un HttpResponse objeto, se aplicará la respuesta de plantilla y el middleware de respuesta y la respuesta resultante se devolverá al navegador. De lo contrario, manejo de excepciones predeterminado patadas en.

Nuevamente, el middleware se ejecuta en orden inverso durante la fase de respuesta, que incluye process_exception. Si un middleware de excepción devuelve una respuesta, el process_exception métodos de las clases de middleware por encima de ese middleware no se llamará en absoluto.

process_template_response()

process_template_response(request, response)

request es un HttpRequest objeto. response es el TemplateResponse objeto (o equivalente) devuelto por una vista de Django o por un middleware.

process_template_response() se llama justo después de que la vista ha terminado de ejecutarse, si la instancia de respuesta tiene un render() método, lo que indica que es un TemplateResponse o equivalente.

Debe devolver un objeto de respuesta que implemente un render método. Podría alterar lo dado response cambiando response.template_name y response.context_datao podría crear y devolver un nuevo TemplateResponse o equivalente.

No es necesario generar respuestas explícitamente; las respuestas se procesarán automáticamente una vez que se haya llamado a todo el middleware de respuesta de la plantilla.

El middleware se ejecuta en orden inverso durante la fase de respuesta, que incluye process_template_response().

Lidiar con las respuestas en tiempo real

diferente a HttpResponse, StreamingHttpResponse no tiene una content attribute. Como resultado, el middleware ya no puede asumir que todas las respuestas tendrán un content attribute. Si necesitan acceder al contenido, deben probar las respuestas de transmisión y ajustar su comportamiento en consecuencia:

if response.streaming:
    response.streaming_content = wrap_streaming_content(response.streaming_content)else:
    response.content = alter_content(response.content)

Nota

streaming_content debe suponerse que es demasiado grande para guardarlo en la memoria. El middleware de respuesta puede envolverlo en un nuevo generador, pero no debe consumirlo. La envoltura se implementa normalmente de la siguiente manera:

defwrap_streaming_content(content):for chunk in content:yield alter_content(chunk)

Manejo de excepciones

Django convierte automáticamente las excepciones generadas por la vista o por el middleware en una respuesta HTTP apropiada con un código de estado de error. Ciertas excepciones se convierten en códigos de estado 4xx, mientras que una excepción desconocida se convierte en un código de estado 500.

Esta conversión tiene lugar antes y después de cada middleware (puede pensar en ella como la película delgada entre cada capa de la cebolla), de modo que cada middleware siempre puede confiar en obtener algún tipo de respuesta HTTP al llamar a su get_response invocable. Middleware no necesita preocuparse por envolver su llamada a get_response en un try/except y manejar una excepción que podría haber sido provocada por un middleware posterior o la vista. Incluso si el siguiente middleware de la cadena genera un Http404 excepción, por ejemplo, su middleware no verá esa excepción; en su lugar obtendrá un HttpResponse objeto con un status_code de 404.

Puedes configurar DEBUG_PROPAGATE_EXCEPTIONS para True para omitir esta conversión y propagar las excepciones hacia arriba.

Soporte asincrónico

Nuevo en Django 3.1.

El middleware puede admitir cualquier combinación de solicitudes sincrónicas y asincrónicas. Django adaptará las solicitudes para que se ajusten a los requisitos del middleware si no puede admitir ambos, pero con una penalización de rendimiento.

De forma predeterminada, Django asume que su middleware es capaz de manejar solo solicitudes sincrónicas. Para cambiar estas suposiciones, establezca lo siguiente attributes en su función o clase de fábrica de middleware:

  • sync_capable es un booleano que indica si el middleware puede manejar solicitudes sincrónicas. Predeterminado a True.
  • async_capable es un booleano que indica si el middleware puede manejar solicitudes asincrónicas. Predeterminado a False.

Si su middleware tiene ambos sync_capable = True y async_capable = True, luego Django le pasará la solicitud sin convertirla. En este caso, puede averiguar si su middleware recibirá solicitudes asíncronas comprobando si el get_response objeto que se le pasa es una función de rutina, utilizando asyncio.iscoroutinefunction().

los django.utils.decorators el módulo contiene sync_only_middleware(), async_only_middleware(), y sync_and_async_middleware() decoradores que te permitan aplique estos indicadores a las funciones de fábrica de middleware.

El invocable devuelto debe coincidir con la naturaleza sincronizada o asincrónica del get_response método. Si tiene un asincrónico get_response, debe devolver una función de rutina (async def).

process_view, process_template_response y process_exception Los métodos, si se proporcionan, también deben adaptarse para que coincidan con el modo sync / async. Sin embargo, Django los adaptará individualmente según sea necesario si usted no lo hace, con una penalización de rendimiento adicional.

A continuación, se muestra un ejemplo de cómo crear una función de middleware que admita ambos:

import asyncio
from django.utils.decorators import sync_and_async_middleware

@sync_and_async_middlewaredefsimple_middleware(get_response):# One-time configuration and initialization goes here.if asyncio.iscoroutinefunction(get_response):asyncdefmiddleware(request):# Do something here!
            response =await get_response(request)return response

    else:defmiddleware(request):# Do something here!
            response = get_response(request)return response

    return middleware

Nota

Si declara un middleware híbrido que admite llamadas sincrónicas y asincrónicas, es posible que el tipo de llamada que reciba no coincida con la vista subyacente. Django optimizará la pila de llamadas de middleware para tener la menor cantidad posible de transiciones sincronizadas / asíncronas.

Por lo tanto, incluso si está ajustando una vista asíncrona, es posible que se le llame en modo de sincronización si hay otro middleware sincrónico entre usted y la vista.

Actualización de middleware de estilo anterior a Django 1.10

class django.utils.deprecation.MiddlewareMixin

Django proporciona django.utils.deprecation.MiddlewareMixin para facilitar la creación de clases de middleware que sean compatibles con ambos MIDDLEWARE y el viejo MIDDLEWARE_CLASSESy admite solicitudes sincrónicas y asincrónicas. Todas las clases de middleware incluidas con Django son compatibles con ambas configuraciones.

El mixin proporciona una __init__() método que requiere un get_response argumento y lo almacena en self.get_response.

los __call__() método:

  1. Llamadas self.process_request(request) (si está definido).
  2. Llamadas self.get_response(request) para obtener la respuesta del middleware posterior y la vista.
  3. Llamadas self.process_response(request, response) (si está definido).
  4. Devuelve la respuesta.

Si se usa con MIDDLEWARE_CLASSES, los __call__() el método nunca se utilizará; Llamadas de Django process_request() y process_response() directamente.

En la mayoría de los casos, heredar de esta combinación será suficiente para hacer que un middleware antiguo sea compatible con el nuevo sistema con suficiente compatibilidad con versiones anteriores. La nueva semántica de cortocircuito será inofensiva o incluso beneficiosa para el middleware existente. En algunos casos, una clase de middleware puede necesitar algunos cambios para adaptarse a la nueva semántica.

Estas son las diferencias de comportamiento entre el uso MIDDLEWARE y MIDDLEWARE_CLASSES:

  1. Debajo MIDDLEWARE_CLASSES, cada middleware siempre tendrá su process_response método llamado, incluso si un middleware anterior cortocircuitó al devolver una respuesta de su process_request método. Debajo MIDDLEWARE, el middleware se comporta más como una cebolla: las capas por las que pasa una respuesta al salir son las mismas capas que vieron la solicitud al entrar. Si un middleware tiene un cortocircuito, solo ese middleware y los anteriores en MIDDLEWARE verá la respuesta.
  2. Debajo MIDDLEWARE_CLASSES, process_exception se aplica a las excepciones generadas desde un middleware process_request método. Debajo MIDDLEWARE, process_exception se aplica solo a las excepciones planteadas desde la vista (o desde la render método de un TemplateResponse). Las excepciones generadas por un middleware se convierten en la respuesta HTTP adecuada y luego se pasan al siguiente middleware.
  3. Debajo MIDDLEWARE_CLASSES, si un process_response método genera una excepción, el process_response Los métodos de todos los middleware anteriores se omiten y 500 Internal Server Error La respuesta HTTP siempre se devuelve (incluso si la excepción generada fue, por ejemplo, un Http404). Debajo MIDDLEWARE, una excepción generada por un middleware se convertirá inmediatamente en la respuesta HTTP adecuada, y luego el siguiente middleware en línea verá esa respuesta. El middleware nunca se omite debido a que un middleware genera una excepción.

Cambiado en Django 3.1:

Se agregó soporte para solicitudes asincrónicas al MiddlewareMixin.