Django
middleware
Ricerca…
introduzione
Middleware in Django è un framework che consente al codice di agganciarsi all'elaborazione di risposta / richiesta e di modificare l'input o l'output di Django.
Osservazioni
Il middleware deve essere aggiunto all'elenco settings.py MIDDLEWARE_CLASSES
prima che venga incluso nell'esecuzione. L'elenco predefinito che Django fornisce quando si crea un nuovo progetto è il seguente:
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',
]
Queste sono tutte funzioni che verranno eseguite in ordine su ogni richiesta (una volta prima che raggiunga il codice di visualizzazione in views.py
e una volta in ordine inverso per la richiamata process_response
, prima della versione 1.10). Fanno una varietà di cose come l'iniezione del token Cross Site Request Forgery (csrf) .
L'ordine è importante perché se un middleware esegue un reindirizzamento, il middleware successivo non verrà mai eseguito. O se un middleware si aspetta che il token csrf sia lì, deve essere eseguito dopo CsrfViewMiddleware
.
Aggiungi dati alle richieste
Django rende davvero facile aggiungere ulteriori dati alle richieste per l'uso all'interno della vista. Ad esempio, possiamo analizzare il sottodominio sul META della richiesta e collegarlo come una proprietà separata sulla richiesta utilizzando il 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]
Se si aggiungono dati con middleware alla richiesta, è possibile accedere a tali dati aggiunti di recente lungo la linea. Qui utilizzeremo il sottodominio analizzato per determinare qualcosa di simile a quale organizzazione sta accedendo alla tua applicazione. Questo approccio è utile per le app distribuite con un'impostazione DNS con sottodomini jolly che puntano tutti a una singola istanza e la persona che accede all'app desidera che una versione sottoposta a skin sia dipendente dal punto di accesso.
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
Ricorda che l'ordine conta quando il middleware dipende l'uno dall'altro. Per le richieste, è necessario che il middleware dipendente venga inserito dopo la dipendenza.
MIDDLEWARE_CLASSES = [
...
'myapp.middleware.SubdomainMiddleware',
'myapp.middleware.OrganizationMiddleware',
...
]
Middleware da filtrare per indirizzo IP
Primo: la struttura del percorso
Se non ce l'hai, devi creare la cartella middleware all'interno della tua app seguendo la struttura:
yourproject/yourapp/middleware
Il middleware delle cartelle deve essere inserito nella stessa cartella di settings.py, urls, templates ...
Importante: non dimenticare di creare il file init .py vuoto all'interno della cartella middleware in modo che la tua app riconosca questa cartella
Invece di avere una cartella separata contenente le classi del middleware, è anche possibile mettere le tue funzioni in un singolo file, yourproject/yourapp/middleware.py
.
Secondo: creare il middleware
Ora dovremmo creare un file per il nostro middleware personalizzato. In questo esempio supponiamo di volere un middleware che filtra gli utenti in base al loro indirizzo IP, creiamo un file chiamato 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
Terzo: aggiungi il middleware nel nostro 'settings.py'
Abbiamo bisogno di cercare il MIDDLEWARE_CLASSES
all'interno di settings.py e lì dobbiamo aggiungere il nostro middleware ( aggiungilo nell'ultima posizione ). Dovrebbe essere come:
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'
)
Fatto! Ora ogni richiesta da ogni cliente chiamerà il tuo middleware personalizzato ed elaborerà il tuo codice personalizzato!
Gestione globale delle eccezioni
Supponiamo che tu abbia implementato una logica per rilevare i tentativi di modificare un oggetto nel database mentre il client che ha presentato le modifiche non ha avuto le ultime modifiche. In tal caso, si ConfictError(detailed_message)
un'eccezione personalizzata ConfictError(detailed_message)
.
Ora si desidera restituire un codice di stato HTTP 409 (Confict) quando si verifica questo errore. In genere, puoi usarlo come middleware invece di gestirlo in ogni vista che potrebbe sollevare questa eccezione.
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)
Comprensione del nuovo stile del middleware Django 1.10
Django 1.10 ha introdotto un nuovo stile middleware in cui process_request
e process_response
sono uniti.
In questo nuovo stile, un middleware è un callable che restituisce un altro callable . Bene, in realtà il primo è una fabbrica di middleware e il secondo è il vero middleware .
La factory middleware prende come argomento singolo il middleware successivo nello stack middleware o la vista stessa quando viene raggiunto il fondo dello stack.
Il middleware accetta la richiesta come argomento singolo e restituisce sempre un HttpResponse
.
Il miglior esempio per illustrare come funziona il middleware di nuovo stile è probabilmente quello di mostrare come creare un middleware compatibile con le versioni precedenti:
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