L’infrastructure des middlewares est un système d’incrustation dans le traitement des requêtes et des réponses de Django. C’est un système de greffons léger et de bas niveau qui permet de modifier globalement les entrées et les sorties de Django.
Chaque composant middleware est responsable d’effectuer une certaine tâche. Par exemple, Django contient un composant middleware, XViewMiddleware, qui ajoute un en-tête "X-View" à chaque réponse à une requête HEAD.
Ce document explique le fonctionnement des middlewares, comment les activer et comment écrire vos propres middlewares. Django est livré avec certains middlewares intégrés qui sont directement utilisables ; ils sont documentés dans la référence des middlewares intégrés.
Pour activer un composant middleware, il faut l’ajouter à la liste MIDDLEWARE_CLASSES de vos réglages Django. Dans MIDDLEWARE_CLASSES, chaque composant middleware est représenté par une chaîne : le chemin Python complet vers le nom de la classe du middleware. Par exemple, voici le réglage MIDDLEWARE_CLASSES créé par défaut par django-admin.py startproject:
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',
)
Pendant les phases de requête (middleware process_request() et process_view()), Django applique les middlewares dans l’ordre dans lequel ils sont définis dans MIDDLEWARE_CLASSES. Durant les phases de réponse (middleware process_response() et process_exception()), les classes sont appliquées dans l’ordre inverse. Vous pouvez imaginer cela comme un oignon : chaque classe middleware est une « couche » qui enveloppe la vue :
Une installation Django n’a pas absolument besoin d’un middleware, ce qui veut dire que MIDDLEWARE_CLASSES peut être vide si vous le voulez, mais il est fortement recommandé d’utiliser au minimum CommonMiddleware.
Il est facile d’écrire un middleware. Chaque composant middleware est une classe Python unique qui définit l’une ou plusieurs des méthodes suivantes :
request est un objet HttpRequest. Cette méthode est appelée pour chaque requête, avant que Django ne décide quelle vue exécuter.
process_request() doit renvoyer soit None, soit un objet HttpResponse. Si elle renvoie None, Django continue le traitement de la requête, en appliquant d’éventuels autres middlewares, puis en exécutant la vue correspondante. Si elle renvoie un objet HttpResponse, Django n’applique plus aucun middleware de requête, de vue ou d’exception, ni la vue correspondante ; il renvoie simplement cet objet HttpResponse. Les middlewares de réponse sont toujours appelés pour chaque réponse.
request est un objet HttpRequest. view_func et la fonction python que Django s’apprête à utiliser (il s’agit bien de l’objet fonction, pas du nom textuel de la fonction). view_args est une liste de paramètres positionnels qui seront transmis à la vue, et view_kwargs est un dictionnaire de paramètres nommés qui seront transmis à la vue. Ni view_args ni view_kwargs ne comprennent le premier paramètre de la vue (request).
process_view() est appelée juste avant que Django n’appelle la vue. Elle doit renvoyer soit None, soit un objet HttpResponse. Si elle renvoie None, Django continue le traitement de la requête, en appliquant d’éventuels autres middlewares possédant process_view(), puis en exécutant la vue correspondante. Si elle renvoie un objet HttpResponse, Django n’applique plus aucun middleware de requête, de vue ou d’exception, ni la vue correspondante ; il renvoie simplement cet objet HttpResponse. Les middlewares de réponse sont toujours appelés pour chaque réponse.
Note
L’accès à request.POST ou request.REQUEST depuis un middleware dans ses méthodes process_request ou process_view empêche toute vue exécutée après les middlewares de pouvoir modifier les gestionnaires de téléversement de la requête, et devrait donc être évité.
La classe CsrfViewMiddleware peut être considérée comme une exception, car elle fournit les décorateurs csrf_exempt() et csrf_protect() qui permettent aux vues de contrôler explicitement le moment de la validation CSRF.
request est un objet HttpRequest. response est une sous-classe de SimpleTemplateResponse (par ex. TemplateResponse) ou tout autre objet réponse qui implémente une méthode render.
process_template_response() doit renvoyer un objet réponse qui implémente une méthode render. Elle peut modifier la réponse donnée en changeant response.template_name et response.context_data, ou elle peut créer et renvoyer une toute nouvelle classe SimpleTemplateResponse ou un équivalent.
process_template_response() ne sera appelée que si l’instance réponse possède une méthode render(), ce qui laisse penser qu’il s’agit d’un objet TemplateResponse ou d’un équivalent.
Il n’est pas nécessaire d’effectuer explicitement le rendu des réponses, celles-ci étant automatiquement « rendues » après que tous les middleware de réponse ont été appelés.
Les middlewares sont exécutés dans l’ordre inverse lors de la phase de réponse, ce qui inclut process_template_response.
request est un objet HttpRequest. response est l’objet HttpResponse renvoyé par une vue Django.
process_response() doit renvoyer un objet HttpResponse. Il peut modifier la réponse donnée ou il peut créer et renvoyer une toute nouvelle instance de HttpResponse.
Au contraire des méthodes process_request() et process_view(), la méthode process_response() est toujours appelée, même si les méthodes process_request() et process_view() de la même classe de middleware ont été court-circuitées parce qu’une méthode de middleware précédente a renvoyé une HttpResponse (ce qui implique par exemple que votre méthode process_response() ne peut pas compter sur une configuration créée dans process_request()). De plus, durant la phase de réponse, les classes sont appliquées dans l’ordre inverse. Cela signifie que les classes définies à la fin de MIDDLEWARE_CLASSES sont exécutées en premier.
Au contraire de HttpResponse, StreamingHttpResponse ne possède pas d’attribut content. En conséquence, les middlewares ne peuvent plus compter sur le fait que toutes les réponses possèdent un attribut content. S’ils ont besoin d’accéder au contenu, ils doivent savoir s’ils ont affaire avec une réponse de type flux (streaming) et ajuster leur comportement en fonction de cela :
if response.streaming:
response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
response.content = wrap_content(response.content)
Il faut toujours partir du principe que streaming_content est trop volumineux pour être stocké en mémoire. Les middleware peuvent l’adapter dans un nouveau générateur, mais ils ne devraient pas le consommer.
request est un objet HttpRequest. exception est un objet Exception généré par la fonction de vue.
Django appelle process_exception() lorsqu’une vue lève une exception. process_exception() doit renvoyer None ou un objet HttpResponse. Si elle renvoie un objet HttpResponse, la réponse sera renvoyée au navigateur. Sinon, la gestion par défaut des exceptions intervient.
Encore une fois, les middlewares sont exécutés dans l’ordre inverse lors de la phase de réponse, ce qui comprend process_exception. Si un middleware d’exception renvoie une réponse, les classes de middleware situées au-dessus ne seront pas du tout appelées.
La plupart des classes de middleware ne nécessitent pas d’initialisation dans la mesure où elles sont essentiellement des conteneurs pour les méthodes process_*. Si vous avez besoin d’une notion d’état global, il est possible d’utiliser __init__ pour cela. Cependant, gardez à l’esprit ces restrictions :
Django initialise les middlewares sans paramètre, il n’est donc pas possible de définir des paramètres obligatoires à __init__.
Contrairement aux méthodes process_* qui sont appelées une fois par requête, __init__ n’est appelée qu’une seule fois, lorsque le serveur Web répond à la première requête.
Il est parfois utile de déterminer au moment de l’exécution si un middleware doit être utilisé. Dans ces cas, la méthode __init__ du middleware peut lever django.core.exceptions.MiddlewareNotUsed. Django se charge alors d’enlever ce middleware du processus des middlewares.
Les classes de middleware n’ont aucune obligation d’héritage.
Une classe de middleware peut se trouver n’importe où dans le chemin Python. Tout ce que Django demande, c’est que le réglage MIDDLEWARE_CLASSES contienne le chemin du middleware.
N’hésitez pas à examiner les middleware disponibles dans Django pour avoir des exemples.
Si vous écrivez un composant middleware que vous pensez susceptible d’être utilisé par d’autres personnes, contribuez à la communauté ! Faites-le nous savoir et nous considérerons son éventuel ajout dans Django.
Jan 13, 2016