Intergiciels (« Middleware »)

Les intergiciels représentent un système de points d’entrée 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 pour modifier de façon globale les entrées et sorties HTTP de Django.

Chaque composant d’intergiciel est responsable d’effectuer une tâche spécifique. Par exemple, Django contient un intergiciel, AuthenticationMiddleware, qui associe les utilisateurs aux requêtes et aux sessions.

Ce document explique comment fonctionnent les intergiciels, la manière de les activer et d’écrire vos propres intergiciels. Django est livré avec quelques intergiciels intégrés qu’il est possible d’utiliser tels quels. Ils sont documentés dans la référence des intergiciels intégrés.

Activation des intergiciels

Pour activer un intergiciel, ajoutez son chemin dans la liste MIDDLEWARE_CLASSES de vos réglages Django.

Dans MIDDLEWARE_CLASSES, chaque intergiciel est représenté par une chaîne : le chemin Python complet vers le nom de la classe d’intergiciel. Par exemple, voici la valeur par défaut créée par django-admin startproject:

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

Une installation de Django n’a pas nécessairement des intergiciels, MIDDLEWARE_CLASSES pouvant très bien être vide si vous le souhaitez, mais il est fortement recommandé d’utiliser au moins CommonMiddleware.

L’ordre dans MIDDLEWARE_CLASSES a son importance car un intergiciel peut dépendre d’un autre. Par exemple, AuthenticationMiddleware stocke l’utilisateur non authentifié dans la session ; il doit donc être exécuté après SessionMiddleware. Voir Ordre des intergiciels pour d’autres indications utiles concernant l’ordre des classes d’intergiciel de Django.

Points d’entrée et ordre des applications

Durant la phase de requête, avant d’appeler la vue, Django applique les intergiciels dans l’ordre où ils sont définis dans MIDDLEWARE_CLASSES, de haut en bas. Deux points d’entrée sont disponibles :

Durant la phase de réponse, après avoir appelé la vue, Django applique les intergiciels dans l’ordre inverse de leur définition, de bas en haut. Trois points d’entrée sont disponibles :

middleware application order

Si vous préférez, on peut comparer ce système à un oignon : chaque classe d’intergiciel est une « pelure » qui englobe la vue.

Le comportement de chaque point d’entrée est décrit ci-dessous.

Écriture de son propre intergiciel

Il est facile d’écrire un intergiciel. Chaque intergiciel est une classe Python unique qui définit l’une ou plusieurs des méthodes suivantes :

process_request()

process_request(request)

request est un objet HttpRequest.

process_request() est appelée pour chaque requête, avant que Django ne décide quelle vue exécuter.

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 intergiciels process_request(), puis les intergiciels process_view() et pour terminer exécute la vue appropriée. Si elle renvoie un objet HttpResponse, Django n’applique plus aucun intergiciel de requête, de vue ou d’exception, ni la vue correspondante ; il va simplement appliquer les intergiciels de réponse à cet objet HttpResponse et renvoyer le résultat.

process_view()

process_view(request, view_func, view_args, view_kwargs)

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 intergiciels process_view(), puis exécute la vue appropriée. Si elle renvoie un objet HttpResponse, Django n’applique plus aucun intergiciel de vue ou d’exception, ni la vue correspondante ; il va simplement appliquer les intergiciels de réponse à cet objet HttpResponse et renvoyer le résultat.

Note

L’accès à request.POST depuis un intergiciel dans ses méthodes process_request ou process_view empêche toute vue exécutée après les intergiciels 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.

process_template_response()

process_template_response(request, response)

request est un objet HttpRequest. response est l’objet TemplateResponse (ou équivalent) renvoyé par une vue Django ou par un intergiciel.

process_template_response() est appelée juste après la fin de l’exécution de la vue, pour autant que 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.

Elle 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 TemplateResponse ou 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 intergiciels de réponse ont été appelés.

Les intergiciels sont exécutés dans l’ordre inverse lors de la phase de réponse, ce qui inclut process_template_response().

process_response()

process_response(request, response)

request est un objet HttpRequest. response est l’objet HttpResponse ou StreamingHttpResponse renvoyé par une vue Django ou par un intergiciel.

process_response() est appelée pour toutes les réponses avant qu’elles soient renvoyées au navigateur.

Elle doit renvoyer un objet HttpResponse ou StreamingHttpResponse. Elle peut modifier la réponse donnée ou elle peut créer et renvoyer une toute nouvelle instance de HttpResponse ou de StreamingHttpResponse.

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 d’intergiciel ont été court-circuitées (parce qu’une méthode d’intergiciel précédente a renvoyé une réponse HttpResponse). En particulier, ceci implique que votre méthode process_response() ne peut pas compter sur une configuration créée dans process_request().

Pour terminer, rappelez-vous que durant la phase de réponse, les intergiciels sont appliqués dans l’ordre inverse, de bas en haut. Cela signifie que les classes définies à la fin de MIDDLEWARE_CLASSES sont exécutées en premier.

Gestion des réponses en flux

Au contraire de HttpResponse, StreamingHttpResponse ne possède pas d’attribut content. En conséquence, les intergiciels 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 = alter_content(response.content)

Note

Il faut toujours partir du principe que streaming_content est trop volumineux pour être stocké en mémoire. Les intergiciels de réponse peuvent l’adapter dans un nouveau générateur, mais ils ne devraient pas le consommer. L’adaptation s’implémente typiquement de cette façon :

def wrap_streaming_content(content):
    for chunk in content:
        yield alter_content(chunk)

process_exception()

process_exception(request, exception)

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 génère une exception. process_exception() doit renvoyer None ou un objet HttpResponse. Si elle renvoie un objet HttpResponse, les intergiciels de réponse par gabarit et de réponse sont appliqués, et la réponse résultante sera renvoyée au navigateur. Sinon, la gestion par défaut des exceptions intervient.

Encore une fois, les intergiciels sont exécutés dans l’ordre inverse lors de la phase de réponse, ce qui comprend process_exception. Si un intergiciel d’exception renvoie une réponse, les classes d’intergiciels situées au-dessus ne seront pas du tout appelées.

__init__()

La plupart des classes d’intergiciels 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 intergiciels 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.

Signalement d’un intergiciel à exclure

Il est parfois utile de déterminer au moment de l’exécution si un intergiciel doit être utilisé. Dans ces cas, la méthode __init__ de l’intergiciel peut générer l’exception django.core.exceptions.MiddlewareNotUsed. Django se charge alors d’enlever cet intergiciel du processus des intergiciels et un message de débogage est journalisé vers django.request lorsque DEBUG est défini à True.

Changed in Django 1.8:

Précédemment, les exceptions MiddlewareNotUsed n’étaient pas journalisées.

Lignes de conduite

  • Les classes d’intergiciels n’ont aucune obligation d’héritage.

  • Une classe d’intergiciel 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 de l’intergiciel.

  • N’hésitez pas à examiner les intergiciels disponibles dans Django pour avoir des exemples.

  • Si vous écrivez un composant intergiciel 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.

Back to Top