Sök…


Introduktion

Middleware i Django är ett ramverk som tillåter kod att ansluta till svar / begäran bearbetning och ändra input eller utgång från Django.

Anmärkningar

Middleware måste läggas till i dina inställningar.py MIDDLEWARE_CLASSES innan den kommer att inkluderas i körningen. Standardlistan som Django tillhandahåller när du skapar ett nytt projekt är följande:

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',
]

Dessa är alla funktioner som körs i ordning på varje begäran (en gång innan den når din visningskod i views.py och en gång i omvänd ordning för process_response återuppringning, före version 1.10). De gör en mängd olika saker som att injicera csrf- symbolen för Cross Site Request Forgery .

Ordningen är viktig eftersom om någon mellanprogramvara omdirigerar, kommer all efterföljande mellanprogram inte att köras. Eller om en middleware förväntar sig att csrf-token är där, måste den köras efter CsrfViewMiddleware .

Lägg till data i förfrågningar

Django gör det riktigt enkelt att lägga till ytterligare data på begäran om användning i vyn. Vi kan till exempel analysera underdomänet på begäran META och bifoga det som en separat egenskap på begäran med hjälp av mellanprogram.

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]

Om du lägger till data med mellanprogram till din begäran kan du komma åt den nyligen tillagda informationen längre ner i raden. Här använder vi det parsade underdomänet för att bestämma något som vilken organisation som får åtkomst till din applikation. Det här tillvägagångssättet är användbart för appar som distribueras med en DNS-inställning med jokerteckenunderdomäner som alla pekar på en enda instans och personen som öppnar appen vill ha en flådad version beroende på åtkomstpunkten.

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

Kom ihåg att ordningen är viktig när du har mellanprogram beror på varandra. För förfrågningar vill du att det beroende mellanprogrammet ska placeras efter beroendet.

MIDDLEWARE_CLASSES = [
    ...
    'myapp.middleware.SubdomainMiddleware',
    'myapp.middleware.OrganizationMiddleware',
    ...
]

Middleware för att filtrera efter IP-adress

Först: Stigstrukturen

Om du inte har det måste du skapa mellanprogrammappen i din app enligt strukturen:

yourproject/yourapp/middleware

Mappens mellanprogram ska placeras i samma mapp som inställningar.py, webbadresser, mallar ...

Viktigt: Glöm inte att skapa den init .py-tomma filen i mellanprogrammappen så att din app känner igen den här mappen

Istället för att ha en separat mapp som innehåller dina mellanvaruklasser är det också möjligt att lägga dina funktioner i en enda fil, yourproject/yourapp/middleware.py .

För det andra: Skapa mellanprogrammet

Nu ska vi skapa en fil för vår anpassade mellanprogram. Låt oss i det här exemplet anta att vi vill ha ett mellanprogram som filtrerar användarna baserat på deras IP-adress, vi skapar en fil som heter 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

Tredje: Lägg till mellanprogrammet i våra "inställningar.py"

Vi måste leta efter MIDDLEWARE_CLASSES inuti settings.py och där måste vi lägga till vårt mellanprogram ( Lägg till det i den sista positionen ). Det borde vara som:

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'
)

Gjort! Nu kommer varje begäran från varje klient att ringa din anpassade mellanprogram och bearbeta din anpassade kod!

Globalt hantering av undantag

Säg att du har implementerat någon logik för att upptäcka försök att modifiera ett objekt i databasen medan klienten som skickade ändringar inte hade de senaste ändringarna. Om ett sådant fall hämtar du ett anpassat undantag ConfictError(detailed_message) .

Nu vill du returnera en HTTP 409 (Confict) statuskod när detta fel inträffar. Du kan vanligtvis använda som mellanprogram för detta istället för att hantera det i varje vy som kan höja detta undantag.

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)

Förstå Django 1.10 mellanvarans nya stil

Django 1.10 introducerade en ny middleware-stil där process_request och process_response slås samman.

I den här nya stilen är en mellanprogramvara en återvinningsbar som returnerar en annan teckningsbar . Tja, faktiskt är den förstnämnda en middleware-fabrik och den senare är den faktiska middleware .

Mellanvarufabriken tar som ett enda argument nästa mellanvaror i mellanvarusbunten eller vyn i sig när bunten av bunten nås.

Mellanprogrammet tar begäran som ett enda argument och returnerar alltid en HttpResponse .

Det bästa exemplet för att illustrera hur ny stil mellanprogram fungerar är förmodligen att visa hur man gör en bakåtkompatibel mellanprogram :

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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow