Zum Inhalt

Middleware

Starlette enthält mehrere Middleware-Klassen zum Hinzufügen von Verhaltensweisen, die für die gesamte Anwendung gelten, die als Standard-ASGI-Middleware-Klassen implementiert sind und auf Starlette oder eine andere ASGI-Anwendung angewendet werden können.

Verwendung von Zwischenräumen

Mit der Starlette-Anwendungsklasse können Sie ASGI-Middleware so einschließen, dass sichergestellt ist, dass sie immer von Ausnahmehandlern eingepackt wird.

von starlette. applications import Starlette
von starlette. midleware import Middleware
von starlette. middleware. httpsredirect import HTTPSRedirectMiddleware
from starlette. middleware. trustedhost import TrustedHostMiddleware

Die Routen….

#Ensure that all requests include an 'example.com' or #Ensure that all requests include an 'example.com' or #Ensure that all requests include an 'example.com' or #Ensure that all requests include an 'example.com' or
#''. example.com''host header, and strictly enforce https-only access.
Mittelfeld []
    Die Midlife-Funktion (
        bei TrustedHostMiddleware,
        allowed_hosts ='example.com','*. example.com'], und
    ),
    Middleware (HTTPSRedirectMiddleware) oder
]

Starlette (routes = routes, middleware = middleware) ist die Abkürzung für "routes".

Standardmäßig enthält jede Starlette-Anwendung automatisch zwei Middleware-Abschnitte:

  • ServerErrorMiddleware-Stellt sicher, dass Anwendungsausnahmen benutzerdefinierte 500-Seiten zurückgeben oder Tracking-Informationen für die Anwendung im DEBUG-Modus anzeigen können, der immer die äußerste Middleware-Schicht ist.
  • ExceptionMiddleware-Fügen Sie Exception-Handler hinzu, so dass ein bestimmter Typ von erwarteten Ausnahmen einer Handlerfunktion zugeordnet werden kann, z. B. führt das Auslösen von HTTPException(status_code=404) innerhalb eines Endpunkts zu einer benutzerdefinierten 404-Seite.

Middleware wird von oben nach unten bewertet, so dass der Ausführungsprozess in unserer Beispielanwendung wie folgt aussehen würde:

  • Das Mittelstück
    • ServerErrorMiddleware
    • TrustedHostMiddleware
    • HTTPSRedirectMiddleware
    • ExceptionMiddleware
  • Route wählen.
  • Endhaltestelle

In Starlette-Paketen können die folgenden Middleware verwendet werden:

CORS Zwischenablage

Fügen Sie den entsprechenden CORS-Header für ausgehende Antworten hinzu, um Cross-Origin-Anfragen von Browsern zu ermöglichen.

Die von der CORSIddleware-Implementierung verwendeten Standardparameter sind standardmäßig restriktiv, daher müssen Sie bestimmte Quellen, Methoden oder Header explizit aktivieren, damit Browser sie in einem domänenübergreifenden Kontext verwenden können.

von starlette. applications import Starlette
von starlette. middleware import Middleware
von starlette. middleware. cors import CORSMiddleware

Die Routen….

Mittelfeld []
    Middleware (CORSMiddleware, allow_origins = Hochhaus '*'])
]

Starlette (routes = routes, middleware = middleware) ist die Abkürzung für "routes".

Folgende Argumente wurden unterstützt:

  • allow_origins - Eine Liste von Quellen, die Cross-Source-Anforderungen zulassen sollten, z. B. ['https://example.org', 'https://www.example.org'] Sie können es verwenden. ['*'] Erlaubt jede Quelle.
  • allow_origin_regex-Eine Zeichenfolge für einen regulären Ausdruck, die mit einer Quelle übereinstimmt, die Cross-Source-Anfragen ausführen soll, z. B.'https://.*\.example\.org'.
  • allow_methods - Eine Liste der HTTP-Methoden, die für Cross-Origin-Anforderungen zugelassen sein sollten, wird standardmäßig angezeigt. ['GET'] Sie können es verwenden. ['*'] Alle Standardmethoden sind erlaubt.
  • allow_headers - Eine Liste von HTTP-Anforderungsheadern, die für Cross-Origin-Anforderungen unterstützt werden sollen, ist standardmäßig aktiviert. [] Sie können verwenden. ['*'] um alle Header für CORS-Anfragen zu erlauben. Accept 、 Accept-Language 、 Content-Language und Content-Type Kopfbedeckung ist immer erlaubt.
  • allow_credentials - Cookies, die angeben, dass Cross Origin Requests unterstützt werden sollen, werden standardmäßig verwendet. False Zusätzlich zu den Belegen, allow_origins 、 allow_methods und allow_headers Kann nicht eingerichtet werden für ['*'] Und sie alle müssen klar definiert werden.
  • expose_headers-Gibt an, dass alle Antwortheader, die dem Browser zugänglich gemacht werden sollen, standardmäßig [] sind.
  • max_age-Die Standardeinstellung für die maximale Zeit, die der Browser zum Caching von CORS-Antworten in Sekunden benötigt, ist 600.

Middleware antwortet auf zwei bestimmte Arten von HTTP-Anforderungen…

Domainübergreifende gemeinsame Nutzung von Ressourcen (CORS) Preflight-Anforderungen

Dies sind alle OPTIONS-Anforderungen mit Origin-und Access-Control-Request-Method-Headern In diesem Fall fängt die Middleware die eingehende Anforderung ab und antwortet mit den entsprechenden CORS-Headern sowie den 200-oder 400-Antworten zu Informationszwecken.

Eine einfache Forderung

Jede trägt Origin In diesem Fall leitet die Middleware die Anforderung wie gewohnt weiter, enthält jedoch den entsprechenden CORS-Header in der Antwort.

Mitten im Gespräch

Fügen Sie HTTP-Sitzungsinformationen hinzu, die auf signierten Cookies basieren, die lesbar, aber nicht änderbar sind.

Verwenden Sie die request.session Dictionary Interface für den Zugriff auf oder die Änderung von Sitzungsdaten.

Folgende Argumente wurden unterstützt:

  • secret_key - Es muss ein zufälliger String sein.
  • session_cookie - Der Standardwert ist "Gespräch".
  • max_age-Sitzungsablaufzeit in Sekunden Der Standardwert ist 2 Wochen Wenn Sie auf None setzen, ist das Cookie für die gleiche Dauer wie die Browsersitzung gültig. Das* same_site-SameSite Flag verhindert, dass Browser bei Cross-Site-Anfragen Sitzungscookies senden, wobei der Standardwert 'lax' ist.
  • path-Der Standardpfad, der für Sitzungscookies festgelegt wird, ist '/'.
  • https_only - Gibt an, dass Sicherheitsflaggen gesetzt werden sollten (nur in Verbindung mit HTTPS verfügbar). False 。
  • domain-Cookie-Domäne, die zum Teilen von Cookies zwischen Subdomains oder Domänen verwendet wird Der Browser legt standardmäßig die Domäne auf demselben Host fest, auf dem Cookies gesetzt sind, mit Ausnahme der Subdomain (Referenz).

    von starlette. applications import Starlette von starlette. middleware import Middleware from starlette. middleware. sessions import sessionMiddleware (engl. für „ Starlette. Middleware. Sitzungen importieren ")

    Die Routen…

    Mittelweg [] Middleware (sessionMiddleware, secret_key =..., https_only = True)(Seite 2) ]

    Starlette (routes = routes, middleware = middleware) ist die Abkürzung für "routes".

HTTPS-Redirection-Middleware aufrufen

Alle eingehenden Anfragen müssen https oder wss sein und alle an http oder ws übergebenen Anfragen werden an das sichere Programm weitergeleitet.

von starlette. applications import Starlette
von starlette. midleware import Middleware
von starlette. middleware. httpsredirect import HTTPSRedirectMiddleware

Die Routen…

Mittelweg []
    Middleware (HTTPSRedirectMiddleware) oder
]

Starlette (routes = routes, middleware = middleware) ist die Abkürzung für "routes".

Für diese Middlewareklasse gibt es keine Konfigurationsoptionen.

Vertrauenswürdige Mittelsmänner

Erzwingt, dass alle eingehenden Anforderungen über einen ordnungsgemäß festgelegten Host-Header verfügen, um HTTP-Host-Header-Angriffe zu verhindern.

von starlette. applications import Starlette
von starlette. middleware import Middleware
from starlette. middleware. trustedhost import TrustedHostMiddleware

Die Routen…

Mittelweg []
    Middleware (TrustedHostMiddleware, allowed_hosts = VMware 'beispiel.com','*. beispiel.com'])
]

Starlette (routes = routes, middleware = middleware) ist die Abkürzung für "routes".

Folgende Argumente wurden unterstützt:

  • allowed_hosts - Eine Liste von Domänennamen, die als Hostnamen zulässig sein sollten, wie z. B. *.example.com Solche Platzhalter-Domänennamen können verwendet werden, um Subdomains abzugleichen Um einen beliebigen Hostnamen zu ermöglichen, können Sie verwenden. allowed_hosts=["*"] Oder man lässt die Mitte weg.
  • www_redirect-Wenn auf True gesetzt, werden Anfragen an nicht-www-Versionen von zulässigen Hosts an ihre www-Version umgeleitet, die standardmäßig True ist.

Wenn eine eingehende Anforderung nicht ordnungsgemäß überprüft wird, wird eine 400-Antwort gesendet.

GZip-Zwischenlösungen

Verarbeitet die GZip-Antwort für jede Anfrage, die "gzip" im Accept-Encoding-Header enthält.

Die Middleware verarbeitet sowohl Standardantworten als auch Stream-Antworten.

von starlette. applications import Starlette
von starlette. midleware import Middleware
from starlette. middleware. gzip import GZipMiddleware (englisch)


Die Routen…

Mittelweg []
    Middleware (GZipMiddleware, minimum_size = 1000 und compresslevel = 9)
]

Starlette (routes = routes, middleware = middleware) ist die Abkürzung für "routes".

Folgende Argumente wurden unterstützt:

  • minimum_size - Bei Antworten, die kleiner als diese minimale Bytegröße sind, führen Sie keine GZip-Komprimierung durch. 500 。
  • compresslevel - Wird während der GZip-Komprimierung verwendet, ist es eine ganze Zahl von 1 bis 9, die standardmäßig verwendet wird. 9 Je niedriger der Wert, desto schneller ist die Komprimierung, aber je größer die Dateigröße ist; je höher der Wert, desto langsamer die Komprimierung, aber desto kleiner die Dateigröße.

Die Middleware komprimiert keine GZip-Responses, die bereits auf Content-Encoding gesetzt sind, um zu verhindern, dass sie zweimal decodiert werden

BaseHTTP-Zwischenspeicher

Eine abstrakte Klasse, mit der Sie ASGI-Middleware für eine Anfrage-/Antwort-Schnittstelle schreiben können.

Die Verwendung von

Um eine Middleware-Klasse mit BaseHTTPMiddleware zu implementieren, müssen Sie die Methode async def dispatch(request, call_next) überschreiben.

von starlette. applications import Starlette
von starlette. middleware import Middleware
from starlette. middleware. base import BaseHTTPIddleware {f}


class CustomHeaderMiddleware (BaseHTTPIddleware):
    async def dispatch (self, request, call_next):(für den Suchbegriff)
        request = await call_next (Anforderung)
        response. headers 'Custom']=' Beispiel '
        return antwort zurück

Die Routen….

Mittelweg []
    Middleware (CustomHeaderMiddleware) Bearbeiten
]

Starlette (routes = routes, middleware = middleware) ist die Abkürzung für "routes".

Wenn Sie Konfigurationsoptionen für Middleware-Klassen bereitstellen möchten, sollten Sie sie neu schreiben. __init__ Stellen Sie sicher, dass der erste Parameter app Alle übrigen Parameter sind optionale Schlüsselwortparameter, wenn Sie dies tun, stellen Sie sicher, dass Sie sie auf der Instanz festlegen. app Eigenschaften.

class CustomHeaderMiddleware (BaseHTTPIddleware):
    def__init__(self, app, header_value ='Example'):
        super ().__init__(für app)
        selbst. header_value = header_value

    async def dispatch (self, request, call_next):(für den Dispatch)
        request = await call_next (Anforderung)
        response . headers ' Custom'] = self . header _ value [ Bearbeiten | Quelltext bearbeiten ]
        return antwort zurück


Mittelweg [ ]
    Middleware (CustomHeaderMiddleware , header _ value ='Customized') (Dateiendung)
]

Starlette (routes = routes , middleware = middleware) ist die Abkürzung für " routes " .

Eine Middleware-Klasse sollte ihren Status nicht außerhalb der __init__-Methode ändern , sondern Sie sollten jeden Zustand entweder lokal in der dispatch-Methode belassen oder explizit übergeben , ohne den Status der Middleware-Instanz zu ändern .

Beschränktheit

Derzeit, BaseHTTPMiddleware Es gibt einige bekannte Einschränkungen:

  • verwendet BaseHTTPMiddleware, um zu verhindern , dass Änderungen an contextlib.ContextVar nach oben übertragen werden , d . h . wenn Sie einen Wert für ContextVar in Ihrem Endpunkt festlegen und versuchen , ihn aus dem Relay zu lesen , werden Sie feststellen , dass sich dieser Wert von dem unterscheidet , den Sie am Endpunkt festgelegt haben (ein Beispiel für dieses Verhalten finden Sie in diesem Test) .

Um diese Einschränkungen zu überwinden , verwenden Sie die reine ASGI-Middleware , wie unten gezeigt .

Reine ASGI-Middleware

Die ASGI-Spezifikation ermöglicht die Implementierung von ASGI-Middleware direkt über die ASGI-Schnittstelle als eine Reihe von ASGI-Anwendungen , die wiederum die nächste aufrufen , genau wie die Middleware-Klasse , die mit Starlette geliefert wird .

Dieser Low-Level-Ansatz bietet mehr Kontrolle über das Verhalten und verbessert die Interoperabilität zwischen Frameworks und Servern. BaseHTTPMiddleware deren Beschränkungen.

Reine ASGI-Middleware erstellen

Die häufigste Methode zum Erstellen von ASGI-Middleware ist die Verwendung einer Klasse .

Klasse ASGiMiddleware :
    def _ _ init _ _ (self , app) : (für sich , app)
        self.app = Die App

    async def _ _ call _ _ (self , scope , receive , send) : async def _ _ call _ _ (self , scope , receive , send) und
        Await sehenself.app(Offenbarung , Offenbarung) .

Die obige Middleware ist die grundlegendste ASGI-Middleware , die eine übergeordnete ASGI-Anwendung als Argument in ihrem Konstruktor erhält und eine async __call__-Methode implementiert , die diese übergeordnete Anwendung aufruft .

Einige Implementierungen , wie asgi-cors, verwenden einen alternativen Stil , der die Funktion verwendet :

Importieren von Funktionen

def asgi _ middleware () () () (Beispiel) :
    def asgi _ decorator (für) :

        @ functools. wraps (App)(Englisch)
        async def wrapped_app (scope, receive, send):
            Await App (Bildschirm, Empfänger, Senden)

        zurück zu Wraped_App

    Zurück zu ASGI_DECORATOR

In jedem Fall muss die ASGI-Middleware ein aufrufbares Objekt sein, das drei Parameter akzeptiert:scope,receive und send.

  • scope ist ein Wörterbuch, das Informationen über Verbindungen enthält, von denen scope["type"]:
    • "http" : Wird für HTTP-Anforderungen verwendet.
    • "websocket" : für WebSocket-Verbindungen.
    • "lifespan" : Wird für ASGI Lifecycle Messages verwendet.
  • receive und send ASGI-Ereignisnachrichten können zum Austausch mit ASGI-Servern verwendet werden-weitere Informationen finden Sie in der ASGI-Spezifikation, in der Typ und Inhalt dieser Nachrichten weiter unten beschrieben werden.

Verwendung von reiner ASGI-Middleware

Reine ASGI-Middleware kann wie jede andere Middleware verwendet werden:

von starlette. applications import Starlette
von starlette. middleware import Middleware

from. middleware import ASGIIddleware importiert von. middleware

Die Routen…

Mittelweg []
    Middleware (ASGiMiddleware) oder
]

app = Starlette (..., Mittelteil = Mittelteil)

Siehe auch die Verwendung von Zwischenräumen.

Typ Anmerkung

Es gibt zwei Möglichkeiten, der Middleware Anmerkungen hinzuzufügen: entweder Starlette selbst oder asgiref.

  • Mit Starlette: Für die meisten gängigen Anwendungsfälle.

    from starlette. types import ASGIApp, Nachricht, Scope, Empfänger, Senden

    Klasse ASGiMiddleware : def _ _ init _ _ (self , app : ASGIApp) - > Keine : self.app = Die App

    async def _ _ call _ _ (self , scope : Scope , receive : Receive , send : Send) - > None :
        if scope [ " type " ] ! = " http " aus :
            return await zurückself.app(Offenbarung , Offenbarung)
    
        async def send _ wrapper (Nachricht : Nachricht) - > None :
            # etwas tun
            Nachricht (await send)
    
        Await sehenself.app(Weitergeleitet von Scope , receive , send _ wrapper)
    
  • verwendet asgiref: um strengere Typhinweise zu implementieren .

    typing import ASGI3Application , ASGIReceiveCallable , ASGISendCallable , Scope (Eingabeaufforderung) from asgiref . typing import ASGIReceiveEvent , ASGIEndEvent (englisch)

    Klasse ASGIIddleware : def _ _ init _ _ (self , app : ASGI3Application) - > Keine : self.app = Die App

    async def _ _ call _ _ (self , scope : Scope , receive : ASGIReceiveCallable , send : ASGIEndCallable) - > None :
        if scope [ " type " ] ! = " http " aus :
            Await sehenself.app(Offenbarung , Offenbarung) .
            return Zurück
    
        async def send _ wrapper (Nachricht : ASGIEndEvent) - > None :
            # etwas tun
            Nachricht (await send)
    
        return await zurückself.app(Weitergeleitet von Scope , receive , send _ wrapper)
    

Übliches Modell

Nur bestimmte Anforderungen bearbeiten

ASGI-Middleware ermöglicht die Anwendung spezifischer Verhaltensweisen basierend auf dem Inhalt von scope.

Wenn Sie zum Beispiel nur HTTP-Anfragen bearbeiten , schreiben Sie ...

Klasse ASGIIddleware :
    def _ _ init _ _ (self , app) : (für sich , app)
        self.app = Die App

    async def _ _ call _ _ (self , scope , receive , send) : async def _ _ call _ _ (self , scope , receive , send) und
        if scope [ " type " ] ! = " http " aus :
            Await sehenself.app(Offenbarung , Offenbarung)
            return Zurück

        # Mach hier etwas !

        Await istself.app(Offenbarung , Offenbarung)

Ebenso ist nur die Middleware für WebSockets auf scope["type"] != "websocket" geschützt .

Middleware kann sich auch je nach Anforderungsmethode , URL oder First-Class - Standard unterschiedlich verhalten .

Wiederherstellen von Starlette-Komponenten

Starlette bietet mehrere Datenstrukturen, die ASGI akzeptieren. scope 、 receive und/oder send Parameter, mit denen Sie auf einer höheren Abstraktionsstufe arbeiten können, umfassen solche Datenstrukturen. Request 、 Headers 、 QueryParams 、 URL Abwarten.

Beispielsweise können Sie ein Request instanziieren , um HTTP-Anforderungen einfacher überprüfen zu können :

from starlette . requests import Request (Eingabeaufforderung)

Klasse ASGIIddleware :
    def _ _ init _ _ (self , app) : (Beispiel)
        self.app = Die App

    async def _ _ call _ _ (self , scope , receive , send) : async def _ _ call _ _ (self , scope , receive , send) und
        if scope [ " type " ] = " http " : wenn Bereich [ " type " ] = " http " ist :
            Request = Request (Vorschlag)
            Verwenden Sie `request.method`,`request.url`,`request.headers`, etc.

        Await istself.app(Offenbarung , Offenbarung)

Sie können auch Antworten wiederverwenden , bei denen es sich um ASGI-Apps handelt .

Eine eifrige Antwort schicken

Mit scope können Sie bedingt verschiedene ASGI-Anwendungen aufrufen - ein Anwendungsfall könnte darin bestehen , die Antwort zu senden , ohne die Anwendung aufzurufen .

Diese Middleware verwendet beispielsweise ein Wörterbuch , um eine dauerhafte Umleitung basierend auf dem Pfad der Anforderung durchzuführen , wenn Sie das Routing-URL - Muster neu gestalten müssen , was verwendet werden kann , um eine kontinuierliche Unterstützung für ältere URLs zu implementieren .

from starlette . datastructures import URL (englisch)
from starlette . responses import RedirectResponse (englisch)

Klasse RedirectsMiddleware :
    def _ _ init _ _ (self , app , path _ mapping : dict) : def _ init _ _ _ (self , app , path _ mapping : dict) :
        self.app = Die App
        Siehe auch : path _ mapping = path _ mapping

    async def _ _ call _ _ (self , scope , receive , send) : async def _ _ call _ _ (self , scope , receive , send) und
        if scope [ " type " ] ! = " http " aus :
            Await sehenself.app(Offenbarung , Offenbarung)
            return Zurück

        url = URL (Bereich = URL)

        if url . path in self . path _ mapping : Wenn die URL . path in self . path _ mapping :
            url = url . replace (path = self . path _ mapping [ url . path ]) [ Bearbeiten | Quelltext bearbeiten ]
            response = RedirectResponse (url , status _ code = 301) Bearbeiten
            Erhalte Antworten (Senden , Empfangen , Senden)
            return Zurück

        Await sehenself.app(Offenbarung , Offenbarung) .

Die Beispielverwendung sieht so aus :

von starlette . applications import Starlette
von starlette . middleware import Middleware

Die Routen … .

Redirections = {Vorlage : Redirections}
    "/v1/resource/" : "/v2/resource/" und "/v2/resource/"
    # ...
}

Mittelweg [ ]
    Middleware (RedirectsMiddleware , path _ mapping = Redirections) und
]

Starlette (routes = routes , middleware = middleware) ist die Abkürzung für " routes " .

Überprüfen oder Ändern von Anforderungen

Ein vollständiges Beispiel dieses Musters kann durch die Operation scope aufgerufen oder geändert werden , siehe ProxyHeadersMiddleware von Uvicorn , das scope überprüft und anpasst , wenn Dienste hinter einem Front-End - Agent bereitgestellt werden .

Darüber hinaus ermöglicht das Verpacken von receive ASGI-aufrufbaren Objekten den Zugriff auf oder Ändern des HTTP-Anforderungstextes , indem Sie die http.request ASGI-Ereignismeldung bearbeiten .

Diese Middleware berechnet und protokolliert beispielsweise die Größe des Subjekts der eingehenden Anforderung …

class LoggedRequestBodySizeMiddleware :
    def _ _ init _ _ (self , app) : (Beispiel)
        self.app = Die App

    async def _ _ call _ _ (self , scope , receive , send) : async def _ _ call _ _ (self , scope , receive , send) und
        if scope [ " type " ] ! = " http " aus :
            Await sehenself.app(Offenbarung , Offenbarung)
            return Zurück

        Größe = 0 _ Größe

        async def receive _ logging _ request _ body _ size () () () : () (auf Englisch)
            lokale Körpergröße {f}

            Nachricht = await receive ()
            assert message [ " type " ] = = " http . request " (Nachricht vom Typ)

            body _ size + = len (message . get (" body " , " b ")) (Nachricht . get (" Körper " , " b "))

            get (" more _ body " , False) :
                Größe des angeforderten Körpers war :{body_size}-Bytes

            return Botschaft

        Await sehenself.app(request _ body _ size , senden)

In ähnlicher Weise kann die WebSocket-Middleware websocket.receive ASGI-Ereignismeldungen verarbeiten , um eingehende WebSocket-Daten zu überprüfen oder zu ändern .

Ein Beispiel für die Änderung des HTTP-Anforderungstextes finden Sie unter msgpack-asgi.

Antworten überprüfen oder ändern

Durch das Verpacken eines aufrufbaren send ASGI-Objekts können Sie die von der zugrunde liegenden Anwendung gesendeten HTTP-Antworten überprüfen oder ändern , indem Sie auf http.response.start- oder http.response.body ASGI-Ereignismeldungen reagieren .

Diese Middleware fügt beispielsweise einige feste zusätzliche Antwortheader hinzu :

from starlette . datastructures import MutableHeaders (Deutsche Übersetzung)

class ExtraResponseHeadersMiddleware :
    def _ _ init _ _ (self , app , headers) (def _ _ init _ _ (self , app , headers) (für Benutzer) :
        self.app Die App
        self . headers = headers (engl . für Kopf)

    async def _ _ call _ _ (self , scope , receive , send) : async def _ _ call _ _ (self , scope , receive , send) und
        if scope [ " type " ] ! = " http " aus :
            return await Zurückself.app(Offenbarung , Offenbarung) .

        async def send _ with _ extra _ headers (Nachricht) :
            if message [ " type " ] = = " http . response . start " :
                headers = MutableHeaders (scope = Nachricht)
                for key , value in self . headers für Schlüssel , Wert in sich selbst . headers :
                    headers . append (Schlüssel , Wert)

            Nachricht (await send)

        Await sehenself.app(Scope , Receive , send _ with _ extra _ headers) (Nachricht , Nachricht , Nachricht)

Siehe auch asgi-logger, das ein Beispiel zum Überprüfen von HTTP-Antworten und zum Aufzeichnen konfigurierbarer HTTP-Zugriffsprotokollzeilen enthält .

In ähnlicher Weise kann die WebSocket-Middleware die ASGI-Ereignismeldung websocket.send verarbeiten , um ausgehende WebSocket-Daten zu überprüfen oder zu ändern .

Wenn Sie den Antworttext ändern , müssen Sie den Antwort-Content-Length- Header aktualisieren , um ihn an die neue Länge des Antworttextes anzupassen , siehe brotli-asgi für ein vollständiges Beispiel .

Informationen bis zum Ende weitergeben

Wenn Sie Informationen mit der zugrunde liegenden Anwendung oder dem Endpunkt teilen müssen , können Sie sie in einem scope-Wörterbuch speichern Beachten Sie , dass dies eine Konvention ist - Starlette verwendet diese Methode zum Teilen von Routing-Informationen mit dem Endpunkt - aber es ist nicht Teil der ASGI-Spezifikation Wenn Sie dies tun , stellen Sie sicher , dass Sie Schlüssel verwenden , die wahrscheinlich nicht von anderen Middleware - oder Anwendungen verwendet werden , um Konflikte zu vermeiden .

Wenn beispielsweise die folgende Middleware enthalten ist , kann der Endpunkt auf request.scope["asgi_transaction_id"] zugreifen .

Der Import von UUUID

Klasse TransactionIDLeware :
    def _ _ init _ _ (self , app) : (Beispiel)
        self.app Die App

    async def _ _ call _ _ (self , scope , receive , send) : async def _ _ call _ _ (self , scope , receive , send) und
        scope [ " asgi _ transaction _ id " ] = uuid.uuid4 () oder
        Await sehenself.app(Offenbarung , Offenbarung)

Reinigung und Fehlbehandlung

Sie können die Anwendung in einem try/except/finally-Block oder Kontext-Manager verpacken , um Reinigungs - oder Fehlerbehandlungen durchzuführen .

Beispielsweise kann die folgende Middleware Metriken erfassen und Anwendungsausnahmen behandeln …

Import Zeit

class MonitoringMiddleware :
    def _ _ init _ _ (self , app) : (Beispiel)
        self.app = Die App

    async def _ _ call _ _ (self , scope , receive , send) : async def _ _ call _ _ (self , scope , receive , send) und
        Zeit = Zeit . Zeit ()
        versuchen :
            Await istself.app(Offenbarung , Offenbarung) .
        Ausnahme als exc :
            # Prozess der Ausnahme
            Raise aufziehen
        Endlich :
            Zeit = Zeit ()
            elapsed = end-start (end-start) Bearbeiten
            #Unterziehen `elapsed` Wie eine Metrik für ein Monitoring-Backend

Siehe auch timing-asgi für ein vollständiges Beispiel dieses Musters.

mögliche Probleme.

Die ASGI Middleware sollte zustandslos sein

Da ASGI gleichzeitige Anforderungen verarbeiten soll, sollte jeder verbindungsspezifische Status auf die __call__-Implementierung beschränkt werden, wenn dies nicht der Fall ist, was in der Regel zu requestübergreifenden Variablen-Lese-/Schreibkonflikten führt und höchstwahrscheinlich zu Fehlern.

Wenn z. B. der X-Mock-Header in der Antwort vorhanden ist, ersetzt dieser Vorgang den Antworttext bedingt.

» machen.

```python
from starlette.datastructures import Headers

class MockResponseBodyMiddleware:
    def __init__(self, app, content):
        self.app = app
        self.content = content

    async def __call__(self, scope, receive, send):
        if scope["type"] != "http":
            await self.app(scope, receive, send)
            return

        # A flag that we will turn `True` if the HTTP response
        # has the 'X-Mock' header.
        # ✅: Scoped to this function.
        should_mock = False

        async def maybe_send_with_mock_content(message):
            nonlocal should_mock

            if message["type"] == "http.response.start":
                headers = Headers(raw=message["headers"])
                should_mock = headers.get("X-Mock") == "1"
                await send(message)

            elif message["type"] == "http.response.body":
                if should_mock:
                    message = {"type": "http.response.body", "body": self.content}
                await send(message)

        await self.app(scope, receive, maybe_send_with_mock_content)
```

» Nein

```python hl_lines="7-8"
from starlette.datastructures import Headers

class MockResponseBodyMiddleware:
    def __init__(self, app, content):
        self.app = app
        self.content = content
        # ❌: This variable would be read and written across requests!
        self.should_mock = False

    async def __call__(self, scope, receive, send):
        if scope["type"] != "http":
            await self.app(scope, receive, send)
            return

        async def maybe_send_with_mock_content(message):
            if message["type"] == "http.response.start":
                headers = Headers(raw=message["headers"])
                self.should_mock = headers.get("X-Mock") == "1"
                await send(message)

            elif message["type"] == "http.response.body":
                if self.should_mock:
                    message = {"type": "http.response.body", "body": self.content}
                await send(message)

        await self.app(scope, receive, maybe_send_with_mock_content)
```

Siehe auch GZipMiddleware für eine vollständige Beispielimplementierung, die dieses potenzielle Problem behebt.

weiter lesen

Dieses Dokument sollte als gute Grundlage für die Erstellung von ASGI-Middleware dienen.

Es gibt jedoch viele gute Artikel zum Thema:

Middleware in anderen Frameworks verwenden

Um ASGI-Middleware um andere ASGI-Anwendungen herum zu verpacken, sollten Sie Anwendungsinstanzen mit einem allgemeineren Schema verpacken:

app = TrustedHostMiddleware (app, allowed_hosts ='beispiel.com'])

Sie können dies auch mit einer Starlette-Anwendungsinstanz tun, aber es ist besser, den middleware=<List of Middleware instances>-Stil zu verwenden, da er Folgendes bewirkt:

  • sorgt dafür, dass alles in einer äußersten Schicht von ServerErrorMiddleware eingewickelt ist.
  • behält die Top-Level-Instanz von app bei.

Anwenden der Middleware auf die Routing-Gruppe

Middleware kann auch einer Mount-Instanz hinzugefügt werden, wodurch Sie die Middleware auf eine Reihe von Routen oder Unteranwendungen anwenden können:

von starlette. applications import Starlette
von starlette. middleware import Middleware
von starlette. middleware. gzip import GZipMiddleware
Routing import Mount, Route von starlette.


Die Bahn […]
    Der Berg (
        "/",
        Die Bahn […]
            Der Weg (
                "Beispiel",
                Endpoint für…
            )
        ],
        middleware =[Middleware (GZipMiddleware)] oder
    )
]

Starlette (Routen = Routen) Bearbeiten

Beachten Sie, dass die auf diese Weise verwendete Middleware nicht wie die auf die Starlette-Anwendung angewendete Middleware für die Ausnahmebehandlung verpackt ist, was im Allgemeinen kein Problem ist, da sie nur für die Überprüfung oder Änderung der Response-Middleware geeignet ist, und selbst dann möchten Sie diese Logik möglicherweise nicht auf die Fehlerantwort anwenden, wenn Sie die Middlewarelogik nur auf bestimmte Routen anwenden möchten, gibt es mehrere Optionen:

  • fügt eine ExceptionMiddleware zu Mount hinzu.
  • fügt einen try/except-Block zu Ihrer Middleware hinzu und gibt von dort eine Fehlermeldung zurück
  • teilt das Markup und die Verarbeitung in zwei Middleware auf, eine auf Mount, die die Antwort als verarbeitet markiert, wie sie benötigt wird (z. B. durch das Festlegen von scope["log-response"] = True), und die andere auf Starlette-Anwendungen, um die Schwerstarbeit zu erledigen.

Die Route/WebSocket-Klasse akzeptiert auch einen middleware-Parameter, mit dem Sie die Middleware auf eine einzige Route anwenden können:

von starlette. applications import Starlette
von starlette. midleware import Middleware
von starlette. middleware. gzip import GZipMiddleware
von starlette. routing import Route


Die Bahn […]
    Der Weg (
        "Beispiel",
        Endpoint für…
        middleware =[Middleware (GZipMiddleware)] oder
    )
]

Starlette (Routen = Routen) Bearbeiten

Sie können Middleware auch auf die Router-Klasse anwenden, wodurch Sie die Middleware auf eine Reihe von Routen anwenden können:

von starlette. applications import Starlette
von starlette. midleware import Middleware
von starlette. middleware. gzip import GZipMiddleware
von starlette. routing import route, Router


Die Bahn […]
    Route ("/beispiel", endpoint =...),
    Route ("/another", endpoint =...), oder
]

Router = Router (Router = Routen, Middleware =[Middleware (GZipMiddleware)])

Drittmittelpunkt

Auth-Github (englisch)

Diese Middleware fügt Authentifizierung für jede ASGI-Anwendung hinzu, bei der Benutzer sich mit ihrem GitHub-Konto (über OAuth) anmelden müssen, und der Zugriff kann auf einen bestimmten Benutzer oder auf Mitglieder einer bestimmten GitHub-Organisation oder-Team beschränkt sein.

Csrf und Asgi-CSRF

Die Middleware zum Schutz vor CSRF-Angriffen implementiert einen Zwei-Wege-Cookie-Modus, in dem ein Cookie gesetzt wird und dann ein Formularfeld mit csrftoken ausgeblendet wird. x-csrftoken HTTP-Header zum Vergleichen.

Authlib für Zwischenprodukte

Ein direkter Ersatz für die Starlette-Sitzungs-Middleware, die das jwt-Modul von authlib verwendet.

Bugsnag Mittelteil

Eine Middleware-Klasse zum Protokollieren von Ausnahmen in Bugsmag.

CSRF-Middleware für

Die Middleware zum Schutz vor CSRF-Angriffen implementiert ein Zwei-Wege-Cookie-Muster, in dem ein Cookie gesetzt und dann mit einem x-csrftoken-HTTP-Header verglichen wird.

Frühzeitige Daten-Middleware

Middleware und Dekoratoren zum Erkennen und Ablehnen von früheren Datenanfragen nach TLSv1.3.

Prometheus Mittelteil

Eine Middleware-Klasse zum Erfassen von Prometheus-Metriken in Bezug auf Anforderungen und Antworten, einschließlich laufender Anforderungen, Zeiten usw.

Der Agenten-Mittelsmann .

Uvicorn enthält eine Middleware-Klasse zur Bestimmung der Client-IP - Adresse auf der Grundlage der X-Forwarded-Proto- und X-Forwarded-For-Header bei der Verwendung eines Proxyservers , die Sie möglicherweise anpassen müssen , um eine komplexere Proxykonfiguration zu erstellen .

Geschwindigkeitsbegrenzungs-Middleware

Eine Middleware mit Geschwindigkeitsbegrenzungen für reguläre Ausdrücke , die mit URLs übereinstimmt ; flexible Regeln ; sehr anpassbar und sehr einfach zu bedienen .

Geforderte mittlere

Middleware-Klasse zum Lesen/Generieren der Anforderungs-ID und Anhängen an das Anwendungsprotokoll .

Rollbar-Zwischenlösungen

Eine Middleware-Klasse zum Protokollieren von Ausnahmen , Fehlern und Protokollnachrichten in der Rollbar .

StarletteOpentracing Bearbeiten

Eine Art Mittelweg , mit demOpenTracing.ioEin kompatibler Tracker sendet Tracking-Informationen und kann zur Analyse und Überwachung verteilter Anwendungen verwendet werden .

Anpassbare Middleware zum Hinzufügen der automatischen Cookie-Verschlüsselung und - Entschlüsselung zu den Starlette-Anwendungen , die zusätzliche Unterstützung für bestehende cookiebasierte Middleware bietet .

Zeitliche Zwischentöne

Eine Middleware-Klasse , die Timing-Informationen für jede Anforderung ausgibt , die sie durchläuft (CPU-Zeit und Wandelzeit) , enthält Beispiele dafür , wie diese Timings als Statsd-Metriken ausgegeben werden können .

mit WSGIIddleware

Eine Middleware-Klasse , die für die Konvertierung einer WSGI-Anwendung in eine ASGI-Anwendung verantwortlich ist .