Recherche…


Introduction

Middleware dans Django est un framework qui permet au code d’accéder au traitement des réponses / requêtes et de modifier l’entrée ou la sortie de Django.

Remarques

Le middleware doit être ajouté à votre liste settings.py MIDDLEWARE_CLASSES avant d'être inclus dans l'exécution. La liste par défaut fournie par Django lors de la création d'un nouveau projet est la suivante:

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

Ce sont toutes les fonctions qui s'exécuteront dans l' ordre à chaque demande (une fois avant d'atteindre votre code de vue dans views.py et une fois dans l'ordre inverse pour le rappel process_response , avant la version 1.10). Ils font une variété de choses telles que l'injection du jeton Cross Site Request Forgery (csrf) .

L'ordre est important car si certains middleware font une redirection, alors tous les middleware suivants ne seront jamais exécutés. Ou si un middleware s'attend à ce que le jeton csrf soit présent, il doit être exécuté après le CsrfViewMiddleware .

Ajouter des données aux requêtes

Django facilite l'ajout de données supplémentaires sur les demandes d'utilisation dans la vue. Par exemple, nous pouvons analyser le sous-domaine sur le META de la demande et le joindre en tant que propriété distincte sur la demande en utilisant un 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 vous ajoutez des données avec un middleware à votre demande, vous pouvez accéder aux nouvelles données ajoutées ultérieurement. Ici, nous utiliserons le sous-domaine analysé pour déterminer quelque chose comme l'organisation accédant à votre application. Cette approche est utile pour les applications déployées avec une configuration DNS avec des sous-domaines génériques qui pointent tous vers une seule instance et la personne qui accède à l'application souhaite qu'une version avec habillage dépend du point d'accès.

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

Rappelez-vous que l'ordre est important lorsque le middleware dépend l'un de l'autre. Pour les requêtes, vous voudrez que le middleware dépendant soit placé après la dépendance.

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

Middleware pour filtrer par adresse IP

Premier: la structure du chemin

Si vous ne l'avez pas, vous devez créer le dossier du middleware dans votre application en suivant la structure:

yourproject/yourapp/middleware

Le middleware de dossier doit être placé dans le même dossier que settings.py, urls, templates ...

Important: n'oubliez pas de créer le fichier vide init .py dans le dossier du middleware afin que votre application reconnaisse ce dossier

Au lieu d'avoir un dossier séparé contenant vos classes de middleware, il est également possible de placer vos fonctions dans un seul fichier, yourproject/yourapp/middleware.py .

Deuxièmement: créer le middleware

Nous devons maintenant créer un fichier pour notre middleware personnalisé. Dans cet exemple, supposons que nous voulons un middleware qui filtre les utilisateurs en fonction de leur adresse IP, nous créons un fichier appelé 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

Troisièmement: ajoutez le middleware dans nos "settings.py"

Nous devons rechercher MIDDLEWARE_CLASSES dans le MIDDLEWARE_CLASSES settings.py et ajouter notre middleware ( ajoutez-le à la dernière position ). Cela devrait être comme:

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

Terminé! Maintenant, chaque demande de chaque client appelle votre middleware personnalisé et traite votre code personnalisé!

Exception de traitement global

Supposons que vous ayez implémenté une logique pour détecter les tentatives de modification d'un objet dans la base de données alors que le client qui a soumis les modifications n'a pas les dernières modifications. Dans ce cas, vous ConfictError(detailed_message) une exception personnalisée ConfictError(detailed_message) .

Maintenant, vous voulez retourner un code d'état HTTP 409 (Confict) lorsque cette erreur se produit. Vous pouvez généralement utiliser comme middleware pour cela au lieu de le gérer dans chaque vue susceptible de générer cette exception.

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)

Comprendre le nouveau style du middleware Django 1.10

Django 1.10 a introduit un nouveau style de middleware où process_request et process_response sont fusionnés.

Dans ce nouveau style, un middleware est un appelant qui renvoie un autre appelable . Eh bien, en réalité, le premier est une usine de middleware et le second est le véritable middleware .

La fabrique de middleware prend comme argument unique le middleware suivant dans la pile des middlewares, ou la vue elle-même lorsque le bas de la pile est atteint.

Le middleware prend la requête en argument unique et retourne toujours un HttpResponse .

Le meilleur exemple pour illustrer le fonctionnement de nouveaux middlewares est probablement de montrer comment créer un middleware à compatibilité descendante:

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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow