Szukaj…


Wprowadzenie

Middleware w Django to framework, który pozwala kodowi podpiąć się do przetwarzania odpowiedzi / żądań i zmienić wejście lub wyjście Django.

Uwagi

Oprogramowanie pośrednie musi zostać dodane do listy settings.py MIDDLEWARE_CLASSES zanim zostanie włączone do wykonania. Domyślna lista udostępniana przez Django podczas tworzenia nowego projektu jest następująca:

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

Są to wszystkie funkcje, które będą uruchamiane w kolejności na każde żądanie (raz, zanim osiągnie swój kod widok w views.py i raz w odwrotnej kolejności do process_response zwrotnego, zanim w wersji 1.10). Robią różne rzeczy, na przykład wstrzykują token Cross Site Request Forgery (csrf) .

Kolejność ma znaczenie, ponieważ jeśli niektóre oprogramowanie pośrednie dokonuje przekierowania, całe następne oprogramowanie pośrednie nigdy nie będzie działać. Lub jeśli oprogramowanie pośrednie oczekuje, że będzie tam token csrf, musi działać po CsrfViewMiddleware .

Dodaj dane do wniosków

Django bardzo ułatwia dodawanie dodatkowych danych do żądań użycia w widoku. Na przykład możemy przeanalizować subdomenę META żądania i dołączyć ją jako osobną właściwość do żądania przy użyciu oprogramowania pośredniego.

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]

Jeśli do żądania dodasz dane z oprogramowaniem pośrednim, możesz uzyskać dostęp do nowo dodanych danych w dalszej części linii. Tutaj użyjemy przeanalizowanej subdomeny, aby określić coś takiego, jaka organizacja uzyskuje dostęp do Twojej aplikacji. To podejście jest przydatne w przypadku aplikacji, które są wdrażane z konfiguracją DNS z poddomenami z symbolami wieloznacznymi, które wszystkie wskazują na jedno wystąpienie, a osoba uzyskująca dostęp do aplikacji chce wersji bez skóry zależnej od punktu dostępu.

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

Pamiętaj, że kolejność ma znaczenie, gdy oprogramowanie pośrednie zależy od siebie. W przypadku żądań należy umieścić zależne oprogramowanie pośrednie po zależności.

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

Oprogramowanie pośrednie do filtrowania według adresu IP

Po pierwsze: struktura ścieżki

Jeśli go nie masz, musisz utworzyć folder oprogramowania pośredniego w aplikacji zgodnie ze strukturą:

yourproject/yourapp/middleware

Oprogramowanie pośrednie folderu należy umieścić w tym samym folderze co settings.py, adresy URL, szablony ...

Ważne: nie zapomnij utworzyć pustego pliku init .py w folderze oprogramowania pośredniego, aby aplikacja rozpoznała ten folder

Zamiast posiadania osobnego folderu zawierającego klasy oprogramowania pośredniego, możliwe jest również umieszczenie funkcji w jednym pliku yourproject/yourapp/middleware.py .

Po drugie: utwórz oprogramowanie pośrednie

Teraz powinniśmy utworzyć plik dla naszego niestandardowego oprogramowania pośredniego. W tym przykładzie załóżmy, że chcemy oprogramowania pośredniego, które filtruje użytkowników na podstawie ich adresu IP, tworzymy plik o nazwie 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

Po trzecie: dodaj oprogramowanie pośrednie w naszym „settings.py”

Musimy poszukać MIDDLEWARE_CLASSES w MIDDLEWARE_CLASSES settings.py i tam musimy dodać nasze oprogramowanie pośrednie ( dodaj je na ostatniej pozycji ). Powinno być jak:

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

Gotowy! Teraz każde żądanie od każdego klienta wywoła niestandardowe oprogramowanie pośrednie i przetworzy kod niestandardowy!

Globalnie wyjątek obsługi

Załóżmy, że zaimplementowano pewną logikę do wykrywania prób modyfikacji obiektu w bazie danych, podczas gdy klient, który przesłał zmiany, nie miał najnowszych modyfikacji. W takim przypadku zgłaszany jest wyjątek niestandardowy ConfictError(detailed_message) .

Teraz chcesz zwrócić kod stanu HTTP 409 (Konflikt), gdy wystąpi ten błąd. Zazwyczaj można do tego użyć jako oprogramowania pośredniego zamiast obsługiwać go w każdym widoku, który może zgłaszać ten wyjątek.

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)

Zrozumienie nowego stylu oprogramowania pośredniego Django 1.10

Django 1.10 wprowadziło nowy styl oprogramowania pośredniego, w którym process_request i process_response są połączone.

W tym nowym stylu oprogramowanie pośrednie to program, który zwraca kolejny program . Cóż, tak naprawdę pierwsza z nich to fabryka oprogramowania pośredniego, a druga to rzeczywiste oprogramowanie pośrednie .

Fabryka oprogramowania pośredniego przyjmuje jako pojedynczy argument następne oprogramowanie pośrednie na stosie oprogramowania pośredniego lub sam widok, gdy osiągnięta zostanie dolna część stosu.

Oprogramowanie pośrednie przyjmuje żądanie jako pojedynczy argument i zawsze zwraca odpowiedź HttpResponse .

Najlepszym przykładem ilustrującym działanie oprogramowania pośredniego w nowym stylu jest prawdopodobnie pokazanie, jak utworzyć oprogramowanie pośrednie zgodne:

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow