Ga naar inhoud

Middleware

Starlette bevat verschillende middleware klassen voor het toevoegen van gedrag dat geldt voor de gehele applicatie. Deze zijn geïmplementeerd als standaard ASGI middleware klassen en kunnen worden toegepast op Starlette of een andere ASGI applicatie.

Middleware gebruiken

Met de Starlette-toepassingsklasse kunt u ASGI-middleware opnemen op een manier die ervoor zorgt dat deze altijd wordt verpakt door uitzonderingshandlers.

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware
Vanaf starlette.middleware.htpsredirect import HTTPSRedirectMiddleware
Vanaf starlette.middleware.trustedhost importeren TrustedHostMiddleware

Routes =...

# Zorg ervoor dat alle verzoeken een example.com 'of
# '*. example.com' host header, en strikt http-only toegang af te dwingen.
Tussenwerk = [
    Tussenwerk (
        TrustedHostMiddleware,
        toegestane _ hosts = ['example.com', '*. example.com'],
    ),
    Middleware (HTTPSRedirectMiddleware)
]

App = Starlette (routes = routes, middleware=middleware)

Standaard bevat elke Starlette-applicatie automatisch twee stukken middleware:

  • ServerErrorMiddleware- Zorgt ervoor dat toepassingsuitzonderingen aangepaste 500 pagina 's kunnen retourneren of traceerinformatie voor de toepassing in DEBUG-modus kunnen weergeven. Dit is altijd de buitenste middlewarelaag.
  • ExceptionMiddleware- Voeg uitzonderingshandlers toe, zodat specifieke soorten verwachte uitzonderingen kunnen worden geassocieerd met handlerfuncties. Als u bijvoorbeeld HTTPException(status_code=404) binnen een eindpunt verhoogt, wordt uiteindelijk een aangepaste 404-pagina weergegeven.

Middleware wordt van boven naar beneden geëvalueerd, dus in onze voorbeeldtoepassing zou de uitvoeringsstroom er als volgt uitzien:

  • middleware
    • ServerErrorMiddleware
    • TrustedHostMiddleware
    • HTTPSRedirectMiddleware
    • ExceptionMiddleware
  • routing
  • eindpunt

De volgende middleware-implementaties zijn beschikbaar in het Starlette-pakket:

CORS Middenwerk

Voeg geschikte CORS-headers toe aan uitgaande reacties om cross-origin-verzoeken van browsers mogelijk te maken.

De standaardparameters die worden gebruikt door CORSMiddleware-implementaties zijn standaard beperkend, dus u moet expliciet specifieke bronnen, methoden of kopteksten inschakelen, zodat browsers ze mogen gebruiken in cross-origin-contexten.

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware
CORSMiddleware importeren vanuit starlette.middleware.cors

Routes =...

Tussenwerk = [
    Middleware (CORSMiddleware, allow _ origins = ['*'])
]

App = Starlette (routes = routes, middleware=middleware)

De volgende argumenten werden gesteund:

  • allow_origins- Lijst van bronnen die toestemming moeten krijgen om cross-origineaanvragen te doen. Bijvoorbeeld ['https://example.org', 'https://www.example.org']. Je kunt ['*'] gebruiken om elke bron toe te staan.
  • allow_origin_regex- Een string voor reguliere expressies die overeenkomt met de bronnen die toestemming zouden moeten krijgen om cross-origineverzoeken te doen. Bijvoorbeeld:'https://.*\.example\.org'.
  • allow_methods- Een lijst met HTTP-methoden die moeten worden toegestaan voor cross-origin-verzoeken. De standaard is ['GET']. U kunt ['*'] gebruiken om alle standaardmethoden toe te staan.
  • allow_headers- Een lijst met HTTP-verzoekkoppen die moeten worden ondersteund voor cross-origin-verzoeken. Standaard is []. U kunt ['*'] gebruiken om alle headers toe te staan. Voor CORS-verzoeken zijn de Accept-,Accept-Language-,Content-Language- en Content-Type-headers altijd toegestaan.
  • allow_credentials- geeft aan dat cookies voor cross-origin-verzoeken moeten worden ondersteund. De standaard is False. Om referenties toe te staan, kunnen allow_origins,allow_methods en allow_headers niet worden ingesteld op ['*'], ze moeten allemaal expliciet worden gespecificeerd.
  • expose_headers- Geeft eventuele antwoordkoppen aan die toegankelijk moeten zijn voor de browser. Standaard is [].
  • max_age- Stelt de maximale tijd in seconden in voor de browser om CORS-reacties in de cache op te slaan. Standaard 600.

Middleware reageert op twee specifieke typen HTTP-verzoeken...

Cross-Origin Resource Sharing (CORS) Precheck-aanvraag

Dit zijn alle OPTIONS-verzoeken met Origin- en Access-Control-Request-Method-headers. In dit geval zal de middleware inkomende verzoeken onderscheppen en reageren met de juiste CORS-headers, evenals een 200 of 400-antwoord voor informatieve doeleinden.

eenvoudig verzoek

Elk verzoek met een Origin-header. In dit geval zal de middleware het verzoek zoals gewoonlijk doorgeven, maar zal de juiste CORS-header in het antwoord opnemen.

gesprek middleware

Voeg HTTP-sessies toe op basis van handtekeningcookies. Sessie-informatie is leesbaar maar niet aanpasbaar.

Gebruik de request.session-woordenboekinterface om sessiegegevens te openen of te wijzigen.

De volgende argumenten werden gesteund:

  • secret_key - Moet een willekeurige string zijn.
  • session_cookie De standaardwaarde is "gesprek".
  • max_age- vervaltijd van de sessie in seconden. Standaard is 2 weken. Indien ingesteld op None, is de geldige periode van de cookie hetzelfde als de duur van de browsersessie. De vlag* same_site- SameSite voorkomt dat de browser sessiecookies verzendt tijdens cross-site verzoeken. De standaard is 'lax'.
  • path- Pad ingesteld voor sessie cookies. Standaard is '/'.
  • https_only - Geeft aan dat er een beveiligingsvlag moet worden ingesteld (alleen voor gebruik met HTTPS). Standaard is False ...
  • domain- Cookie-domein dat wordt gebruikt om cookies te delen tussen subdomeinen of tussen domeinen. Browsers stellen het domein standaard in op dezelfde host waar de cookie is ingesteld, met uitzondering van subdomeinen (referentie).

    Starlette.applicaties importeren vanuit Starlette Middleware importeren vanuit starlette.middleware Vanaf starlette.middleware.sessies importeren SessionMiddleware

    Routes =...

    Tussenwerk = [ Middleware (SessionMiddleware, secret _ key =..., https _ only = Waar) ]

    App = Starlette (routes = routes, middleware=middleware)

HTTPS omleiding middleware

Mandaat dat alle inkomende verzoeken https of wss moeten zijn. Alle inkomende verzoeken aan http of ws worden doorgestuurd naar het beveiligingsschema.

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware
Vanaf starlette.middleware.htpsredirect import HTTPSRedirectMiddleware

Routes =...

Tussenwerk = [
    Middleware (HTTPSRedirectMiddleware)
]

App = Starlette (routes = routes, middleware=middleware)

Er zijn geen configuratie-opties voor deze middleware-klasse.

Vertrouwde host middleware

Forceer alle inkomende verzoeken om de juiste set Host-headers te hebben om te beschermen tegen HTTP-hostheaderaanvallen.

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware
Vanaf starlette.middleware.trustedhost importeren TrustedHostMiddleware

Routes =...

Tussenwerk = [
    Middleware (TrustedHostMiddleware_ hosts = ['example.com', '*. example.com'])
]

App = Starlette (routes = routes, middleware=middleware)

De volgende argumenten werden gesteund:

  • allowed_hosts- Lijst met domeinen die als hostnamen moeten worden toegestaan. Wildcard-domeinen zoals *.example.com kunnen worden gebruikt om subdomeinen te matchen. Om elke hostnaam toe te staan, gebruikt u allowed_hosts=["*"] of laat u middleware weg.
  • www_redirect- Indien ingesteld op Waar worden aanvragen voor niet-www versies van toegestane hosts doorgestuurd naar hun www tegenhangers. Standaard is True.

Als het inkomende verzoek niet goed is gevalideerd, wordt een 400-antwoord verzonden.

GZip Middleware kopen?

Behandelt het GZip-antwoord voor elk verzoek dat "gzip" in de Accept-Encoding-header bevat.

Middleware verwerkt zowel standaard- als streamingreacties.

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware
Vanaf starlette.middleware.gzip importeren GZipMiddleware


Routes =...

Tussenwerk = [
    Middleware (GZipMiddleware, minimum _ size = 1000, compresslevel = 9)
]

App = Starlette (routes = routes, middleware=middleware)

De volgende argumenten werden gesteund:

  • minimum_size- GZip niet voor reacties kleiner dan deze minimale bytegrootte. De standaard is 500.
  • compresslevel- gebruikt tijdens GZip-compressie. Het is een geheel getal van 1 tot 9. De standaardwaarde is 9. Hoe lager de waarde, hoe sneller de compressie, maar hoe groter de bestandsgrootte; hoe hoger de waarde, hoe langzamer de compressie, maar hoe kleiner de bestandsgrootte.

Middleware geeft geen GZip-reacties die Content-Encoding al hebben ingesteld om te voorkomen dat ze tweemaal worden gecodeerd.

BaseHTTP middleware

Een abstracte klasse waarmee u ASGI-middleware kunt schrijven voor de verzoek / antwoordinterface.

gebruik

Om een middleware-klasse te implementeren met BaseHTTPMiddleware, moet u de async def dispatch(request, call_next)-methode overschrijven.

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware
Vanaf starlette.middleware.base importeren BaseHTTPMiddleware


Klasse CustomHeaderMiddleware (BaseHTTPMiddleware):
    Aync def dispatch (zelf, verzoek, call _ next):
        Reactie = afwachten call _ next (verzoek)
        Response.headers ['Aangepast'] = 'Voorbeeld'
        Geef reactie terug

Routes =...

Tussenwerk = [
    Middleware (CustomHeaderMiddleware)
]

App = Starlette (routes = routes, middleware=middleware)

Als u configuratieopties voor middleware-klassen wilt bieden, moet u de __init__-methode overschrijven om ervoor te zorgen dat het eerste argument app is en dat alle resterende argumenten optionele zoekwoordargumenten zijn. Als u dat doet, zorg er dan voor dat u de app-eigenschap op de instantie instelt.

Klasse CustomHeaderMiddleware (BaseHTTPMiddleware):
    Def _ _ init _ _ (zelf, app, header _ value = 'Voorbeeld'):
        Super (). _ _ het _ _ (app)
        Self.header _ waarde = header _ waarde

    Aync def dispatch (zelf, verzoek, call _ next):
        Reactie = afwachten call _ next (verzoek)
        Response.headers ['Aangepast'] = zelf.header _ waarde
        Geef reactie terug


Tussenwerk = [
    Middleware (CustomHeaderMiddleware, header _ value = 'Aangepast')
]

App = Starlette (routes = routes, middleware=middleware)

Middleware-klassen mogen hun status niet wijzigen buiten de __init__-methode. In plaats daarvan moet u elke lokale staat aan de dispatch-methode overlaten of deze expliciet doorgeven, in plaats van de status van de middleware-instantie te wijzigen.

beperking

Momenteel heeft BaseHTTPMiddleware enkele bekende beperkingen:

  • Het gebruik van BaseHTTPMiddleware voorkomt dat wijzigingen in contextlib.ContextVar zich naar boven voortplanten. Dat wil zeggen, als u een waarde voor ContextVar in een eindpunt instelt en probeert deze uit een relais te lezen, zult u zien dat de waarde verschilt van wat u in het eindpunt instelt (zie deze test voor een voorbeeld van dit gedrag).

Gebruik pure ASGI-middleware om deze beperkingen te overwinnen, zoals hieronder weergegeven.

Zuivere ASGI-middenstukken

De ASGI-specificatie maakt het mogelijk om ASGI-middleware rechtstreeks te implementeren met behulp van de ASGI-interface, als een reeks ASGI-applicaties, waarbij de volgende op zijn beurt wordt aangeroepen. In feite is dit de manier waarop de middleware-klassen die met Starlette worden geleverd, worden geïmplementeerd.

Deze benadering op een lager niveau biedt meer controle over gedrag en verbetert de interoperabiliteit tussen frameworks en servers. Het overwint ook de beperkingen van BaseHTTPMiddleware.

Schrijven van pure ASGI middleware

De meest gebruikelijke manier om ASGI-middleware te maken, is door een klasse te gebruiken.

Klasse ASGIMiddleware:
    Def _ _ init _ _ (zelf, app):
        self.app = app

    Aync def _ _ call _ _ (zelf, bereik, ontvangen, verzenden):
        Wacht self.app (scope, ontvangen, verzenden)

De bovenstaande middleware is de meest elementaire ASGI-middleware. Het neemt een ouder ASGI-applicatie als parameter in zijn constructor en implementeert een async __call__-methode die de bovenliggende applicatie aanroept.

Sommige implementaties, zoals asgi-cors, gebruiken een alternatieve stijl, met functies:

Functools importeren

Gebruik asgi _ middleware ():
    Vergelijk asgi _ decorator (app):

        @ functools.wraps (app)
        Aync def wrap _ app (bereik, ontvangen, verzenden):
            Wacht app (scope, ontvangen, verzenden)

        Gewikkelde _ app terugsturen

    Retourneren asgi _ decorator

ASGI-middleware moet echter een opvraagbaar object zijn dat drie parameters accepteert:scope,receive en send.

  • scope is een woordenboek dat informatie bevat over verbindingen, waar scope["type"] kan zijn:
    • "http" : Gebruikt voor HTTP-verzoeken.
    • "websocket" : Gebruikt voor WebSocket verbindingen.
    • "lifespan" : Voor ASGI-levenscyclusberichten.
  • receive en send kunnen worden gebruikt om ASGI-gebeurtenisberichten uit te wisselen met een ASGI-server - meer hierover hieronder. Het type en de inhoud van deze berichten is afhankelijk van het type scope. Meer informatie in de ASGI-specificatie.

Gebruik van pure ASGI middleware

Pure ASGI middleware kan net als elke andere middleware worden gebruikt.

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware

Vanaf middleware importeren ASGIMiddleware

Routes =...

Tussenwerk = [
    Middleware (ASGIMiddleware),
]

Uitgever: App = Starlette (..., middleware=middleware)

Zie ook Middleware gebruiken.

typ annotatie

Er zijn twee manieren om middleware te annoteren: Starlette zelf of asgiref gebruiken.

  • Starlette gebruiken: geschikt voor de meest voorkomende use-cases.

    Van starlette.types importeren ASGIApp, Bericht, Toepassingsgebied, Ontvangen, Verzenden

    Klasse ASGIMiddleware: Def _ _ init _ _ (zelf, app: ASGIApp) - > Geen: self.app = app

    Async def _ _ call _ _ (zelf, scope: Scope, ontvangen: ontvangen, verzenden: verzenden) - > Geen:
        Als scope ["type"]! = "http":
            Retourneren self.app (scope, ontvangen, verzenden)
    
        Async def send _ wrapper (bericht: Bericht) - > Geen:
            #... Doe maar wat
            In afwachting van verzending (bericht)
    
        Wacht self.app (bereik, ontvangen, verzenden _ wrapper)
    
  • gebruikt asgiref: om strengere typehints te bereiken.

    Van asgiref.typing importeren ASGI3Application, ASGIReceiveCallable, ASGISendCallable, toepassingsgebied Van asgiref.typing importeren ASGIReceiveEvent, ASGISendEvent

    Klasse ASGIMiddleware: Def _ _ init _ _ (zelf, app: ASGI3Application) - > Geen: self.app = app

    Async def _ _ call _ _ (zelf, scope: Scope, ontvangen: ASGIReceiveCallable, verzenden: ASGISendCallable) - > Geen:
        Als scope ["type"]! = "http":
            Wacht self.app (scope, ontvangen, verzenden)
            Terugkeer
    
        Async def send _ wrapper (bericht: ASGISendEvent) - > Geen:
            #... Doe maar wat
            In afwachting van verzending (bericht)
    
        Retourneren self.app (bereik, ontvangen, verzenden _ wrapper)
    

gemeenschappelijk patroon

Behandel alleen bepaalde verzoeken

ASGI middleware kan specifiek gedrag toepassen op basis van de inhoud van de scope.

Als u bijvoorbeeld alleen HTTP-verzoeken wilt verwerken, schrijft u...

Klasse ASGIMiddleware:
    Def _ _ init _ _ (zelf, app):
        self.app = app

    Aync def _ _ call _ _ (zelf, bereik, ontvangen, verzenden):
        Als scope ["type"]! = "http":
            Wacht self.app (scope, ontvangen, verzenden)
            Terugkeer

        # Doe hier iets!

        Wacht self.app (scope, ontvangen, verzenden)

Evenzo is alleen WebSocket-middleware beschermd op scope["type"] != "websocket".

Middleware kan zich ook anders gedragen, afhankelijk van de aanvraagmethode, URL, header, enzovoort.

Multiplexen van Starlette-componenten

Starlette biedt verschillende gegevensstructuren die ASGI scope,receive en / of send parameters accepteren, waardoor u op een hoger abstractieniveau kunt werken. Dergelijke gegevensstructuren omvatten Request,Headers,QueryParams,URL, enzovoort.

U kunt bijvoorbeeld een Request instantiëren om HTTP-verzoeken gemakkelijker te controleren.

Van starlette. aanvragen import aanvragen

Klasse ASGIMiddleware:
    Def _ _ init _ _ (zelf, app):
        self.app = app

    Aync def _ _ call _ _ (zelf, bereik, ontvangen, verzenden):
        Als scope ["type"] = = "http":
            Aanvraag = Aanvraag (scope)
            ... # Gebruik `request.method`,`request.url`,`request.headers`, enz.

        Wacht self.app (scope, ontvangen, verzenden)

U kunt ook reacties hergebruiken, die ook ASGI-toepassingen zijn.

Stuur een gretige reactie

Aansluiting controleren scope Hiermee kunt u voorwaardelijk verschillende ASGI-applicaties aanroepen. Een use-case kan zijn om een antwoord te sturen zonder de applicatie te bellen.

Deze middleware gebruikt bijvoorbeeld een woordenboek om een permanente omleiding uit te voeren op basis van het gevraagde pad. Als u het routerings-URL-patroon moet aanpassen, kan dit worden gebruikt om doorlopende ondersteuning voor oudere URL 's te implementeren.

URL geïmporteerd uit starlette.datastructuren
RedirectResponse geïmporteerd uit starlette.reacties

Klasse omleidingenMiddleware:
    Def _ _ init _ _ (zelf, app, pad _ mapping: dict):
        self.app = app
        Zelf.pad _ mapping = pad _ mapping

    Aync def _ _ call _ _ (zelf, bereik, ontvangen, verzenden):
        Als scope ["type"]! = "http":
            Wacht self.app (scope, ontvangen, verzenden)
            Terugkeer

        URL = URL (scope = scope)

        Als url.path in zelf. path _ mapping:
            Url = url. vervangen (pad = self.path _ mapping [url.path])
            Reactie = RedirectResponse (url, status _ code = 301)
            In afwachting van reactie (scope, ontvangen, verzenden)
            Terugkeer

        Wacht self.app (scope, ontvangen, verzenden)

Het voorbeeldgebruik zou er als volgt uitzien:

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware

Routes =...

Omleidingen = {
    "/ v1 / resource /": "/ v2 / resource /",
    # ...
}

Tussenwerk = [
    Middleware (OmleidingMiddleware, pad _ mapping=redirections),
]

App = Starlette (routes = routes, middleware=middleware)

Aanvraag controleren of wijzigen

Verzoekinformatie kan worden geopend of gewijzigd door op scope te werken. Voor een volledig voorbeeld van dit patroon, zie Uvicorn 's ProxyHeadersMiddleware, die scope controleert en aanpast wanneer het achter een front-end proxy wordt geserveerd.

Bovendien kunt u door het receive ASGI-oproepbare object in te pakken de HTTP-aanvraagtekst openen of wijzigen door het http.request ASGI-gebeurtenisbericht te manipuleren.

Deze middleware berekent en registreert bijvoorbeeld de grootte van de inkomende aanvraagtekst...

Klasse LoggedRequestBodySizeMiddleware:
    Def _ _ init _ _ (zelf, app):
        self.app = app

    Aync def _ _ call _ _ (zelf, bereik, ontvangen, verzenden):
        Als scope ["type"]! = "http":
            Wacht self.app (scope, ontvangen, verzenden)
            Terugkeer

        lichaamsgrootte = 0

        Async def ontvangen _ loggen _ verzoek _ lichaam _ grootte ():
            Niet-lokale lichaamsgrootte

            Bericht = afwachten ontvangen ()
            Bevestig bericht ["type"] = = "htp.request"

            body _ size + = len (bericht.get ("lichaam", b "))

            Zo niet message.get ("more _ body", False):
                Afdrukken (f "Grootte van de inhoud van het verzoek was:{body_size}bytes")

            Bericht terugsturen

        Wacht self.app (scope, ontvang _ logging _ request _ body _ size, verzend)

Evenzo kan de WebSocket-middleware de websocket.receive ASGI-gebeurtenisberichten manipuleren om inkomende WebSocket-gegevens te inspecteren of te wijzigen.

Zie msgpack-asgi voor een voorbeeld van het wijzigen van de hoofdtekst van een HTTP-verzoek.

Controleer of wijzig de reactie

Door de send ASGI callable in te pakken, kunt u de HTTP-reactie van de onderliggende applicatie inspecteren of wijzigen. Reageer hiervoor op de http.response.start- of http.response.body ASGI-gebeurtenisberichten.

Deze middleware voegt bijvoorbeeld enkele vaste extra antwoordkoppen toe:

MutableHeaders importeren vanuit starlette.datastructuren

Klasse ExtraResponseHeadersMiddleware:
    Def _ _ init _ _ (zelf, app, headers):
        self.app = app
        Self.headers = kopteksten

    Aync def _ _ call _ _ (zelf, bereik, ontvangen, verzenden):
        Als scope ["type"]! = "http":
            Retourneren self.app (scope, ontvangen, verzenden)

        Async def send _ with _ extra _ headers (bericht):
            Als bericht ["type"] = = "htp.response.start":
                Headers = MutableHeaders (scope = bericht)
                Voor sleutels, waarden in self.headers:
                    Headers.append (sleutel, waarde)

            In afwachting van verzending (bericht)

        Wacht self.app (bereik, ontvangen, verzenden _ met _ extra _ headers)

Zie ook asgi-logger, dat een voorbeeld heeft van het controleren van HTTP-reacties en het loggen van configureerbare HTTP-toegangslogregels.

Evenzo kan WebSocket-middleware de websocket.send ASGI-gebeurtenisberichten manipuleren om uitgaande WebSocket-gegevens te inspecteren of te wijzigen.

Houd er rekening mee dat als u de antwoordtekst wijzigt, u de Content-Length-header van de respons moet bijwerken om overeen te komen met de nieuwe lichaamslengte van de respons. Zie brotli-asgi voor een volledig voorbeeld.

Informatie doorgeven aan het eindpunt

Als u informatie moet delen met de onderliggende applicatie of het eindpunt, kunt u deze opslaan in het scope-woordenboek. Merk op dat dit een conventie is - Starlette gebruikt deze methode bijvoorbeeld om routeringsinformatie te delen met eindpunten - maar het maakt geen deel uit van de ASGI-specificatie. Als u dat doet, zorg er dan voor dat u sleutels gebruikt die waarschijnlijk niet door andere middleware of applicaties worden gebruikt om conflicten te voorkomen.

Een eindpunt heeft bijvoorbeeld toegang tot request.scope["asgi_transaction_id"] wanneer de volgende middleware wordt meegeleverd.

Uid importeren

Klasse TransactionIDMiddleware:
    Def _ _ init _ _ (zelf, app):
        self.app = app

    Aync def _ _ call _ _ (zelf, bereik, ontvangen, verzenden):
        Toepassingsgebied ["asgi _ transactie _ id"] = uuid.uuid4 ()
        Wacht self.app (scope, ontvangen, verzenden)

Schoonmaak en foutafhandeling

U kunt uw toepassing in een try/except/finally-blok of contextmanager verpakken voor opschoning of foutafhandeling.

De volgende middleware kan bijvoorbeeld statistieken verzamelen en uitzonderingen op toepassingen afhandelen...

Import tijd

Klasse MonitoringMiddleware:
    Def _ _ init _ _ (zelf, app):
        self.app = app

    Aync def _ _ call _ _ (zelf, bereik, ontvangen, verzenden):
        Start = tijd.tijd ()
        Probeer:
            Wacht self.app (scope, ontvangen, verzenden)
        Behalve uitzondering als exc:
            ... # Verwerk de uitzondering
            Verhogen
        Eindelijk:
            Einde = tijd.tijd ()
            Verlopen = einde - begin
            ... # Submit `elapsed` als metriek naar een monitoring backend

Raadpleeg ook timing-asgi voor een compleet voorbeeld van dit patroon.

Potentiële problemen

ASGI middleware zou staatloos moeten zijn

Aangezien ASGI is ontworpen om gelijktijdige verzoeken af te handelen, moet elke verbindingsspecifieke status worden beperkt tot __call__ Binnen het bereik van de implementatie. Als u dit niet doet, resulteert dit meestal in variabele lees- / schrijfbotsingen tussen verzoeken en zullen er waarschijnlijk fouten optreden.

Als er bijvoorbeeld een X-Mock-header in de respons aanwezig is, vervangt deze bewerking voorwaardelijk de responslichaam...

✅ doe maar

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

❌ Nee hoor.

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

Raadpleeg ook GZipMiddleware voor een volledige voorbeeldimplementatie die dit potentiële probleem aanpakt.

Verder lezen

Dit document zou voldoende moeten zijn als een goede basis voor het maken van ASGI-middleware.

Er zijn echter veel uitstekende artikelen over dit onderwerp:

Middleware gebruiken in andere frameworks

Om ASGI-middleware rond andere ASGI-toepassingen te wikkelen, moet u een algemener patroon gebruiken voor het inpakken van applicatie-instanties:

App = TrustedHostMiddleware (app, toegestane _ hosts = ['example.com'])

Je kunt dit ook doen met een Starlette-applicatie-instantie, maar het is beter om de middleware=<List of Middleware instances>-stijl te gebruiken omdat deze:

  • zorgt ervoor dat alles is verpakt in een buitenste laag ServerErrorMiddleware.
  • behoudt de app-instantie op het hoogste niveau.

Middleware toepassen op routeringsgroepen

Middleware kan ook worden toegevoegd aan een Mount-instantie, waarmee u middleware kunt toepassen op een set routerings- of subtoepassingen.

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware
Vanaf starlette.middleware.gzip importeren GZipMiddleware
Van starlette. routing importeren Mount, Route


Routes = [
    Monteren (
        "/",
        Routes = [
            Route (
                "/ voorbeeld",
                Eindpunt =...,
            )
        ],
        Middleware = [Tussenproducten (GZipMiddleware)]
    )
]

App = Starlette (routes = routes)

Houd er rekening mee dat de middleware die op deze manier wordt gebruikt, niet is verpakt in middleware met uitzondering, zoals de middleware die wordt toegepast op Starlette-toepassingen. Dit is meestal geen probleem omdat het alleen w**oor het controleren of wijzigen van middleware voor Response, en zelfs dan wilt u deze logica waarschijnlijk niet toepassen op foutreacties. Als u middleware-logica alleen op bepaalde routes op foutreacties wilt toepassen, zijn er een paar opties:

  • Voeg een ExceptionMiddleware toe op Mount
  • voegt een try/except-blok toe aan uw middleware en retourneert daar een foutreactie
  • splitst tagging en verwerking in twee middleware, één geplaatst op Mount die het antwoord markeert als verwerking vereist (bijvoorbeeld door scope["log-response"] = True in te stellen), en de andere toegepast op de Starlette-applicatie om het zware werk te doen.

De Route/WebSocket-klassen accepteren ook een middleware-parameter waarmee u middleware op één enkele route kunt toepassen:

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware
Vanaf starlette.middleware.gzip importeren GZipMiddleware
Van starlette. routing importeren Route


Routes = [
    Route (
        "/ voorbeeld",
        Eindpunt =...,
        Middleware = [Tussenproducten (GZipMiddleware)]
    )
]

App = Starlette (routes = routes)

U kunt ook middleware toepassen op de Router-klasse, waarmee u middleware kunt toepassen op een reeks routes:

Starlette.applicaties importeren vanuit Starlette
Middleware importeren vanuit starlette.middleware
Vanaf starlette.middleware.gzip importeren GZipMiddleware
Van starlette. routing importeren Route, Router


Routes = [
    Route ("/ voorbeeld", endpoint =...),
    Route ("/ een andere", eindpunt =...),
]

Router = Router (routes = routes, middleware = [Middleware (GZipMiddleware)])

middleware van derden

Toevoegen aan github

Deze middleware voegt authenticatie toe aan elke ASGI-applicatie, waarbij gebruikers moeten inloggen met hun GitHub-account (via OAuth). Toegang kan worden beperkt tot specifieke gebruikers of leden van een specifieke GitHub-organisatie of -team.

agi-csrf

Middleware voor bescherming tegen CSRF-aanvallen. Deze middleware implementeert een double-commit cookie-modus waarin een cookie wordt ingesteld en vervolgens wordt vergeleken met een csrftoken verborgen formulierveld of een x-csrftoken HTTP-header.

Middleware schrijven

Een direct alternatief voor de sessie-middleware van Starlette, met behulp van de JWT-module van Authlib.

Bugsnag middleware kopen?

Een middleware-klasse voor het loggen van uitzonderingen op Bugsnag.

CSRF Middenwaren

Middleware om te beschermen tegen CSRF-aanvallen. Deze middleware implementeert een double-commit cookie-modus waarin een cookie wordt ingesteld en vervolgens wordt vergeleken met een x-csrftoken HTTP-header.

Middleware voor vroege gegevens

Middleware en decorateurs voor het detecteren en weigeren van eerdere TLSv1.3-gegevensverzoeken.

Prometheus middleware kopen?

Een middleware-klasse voor het vastleggen van Prometheus-statistieken met betrekking tot verzoeken en reacties, inclusief lopende verzoeken, tijden, enz...

middleware voor proxy header

Uvicorn bevat een middleware-klasse voor het bepalen van IP-adressen aan clientzijde op basis van de X-Forwarded-Proto- en X-Forwarded-For-headers bij gebruik van een proxyserver. Voor complexere proxyconfiguraties moet u deze middleware mogelijk aanpassen.

snelheid beperkende middleware

Een snelheidsbeperkende middleware. Reguliere expressie matching URL 's; flexibele regels; zeer aanpasbaar. Zeer eenvoudig te gebruiken.

Middleware aanvragen

Een middleware-klasse voor het lezen / genereren van verzoek-ID 's en deze aan het toepassingslogboek te koppelen.

Middleware met rolbeugel

Een middleware-klasse voor het loggen van uitzonderingen, fouten en logberichten naar Rollbar.

StarletteOpentracing

Een middleware-klasse die trackinginformatie naar OpenTracing.io-compatibele trackers stuurt en kan worden gebruikt om gedistribueerde applicaties te analyseren en te bewaken.

Aanpasbare middleware voor het toevoegen van automatische cookie-encryptie en decodering aan Starlette-applicaties, die extra ondersteuning biedt voor bestaande cookie-gebaseerde middleware.

timing middleware

Een middleware-klasse die timinginformatie (CPU-tijd en wandkloktijd) uitzendt voor elk verzoek dat er doorheen gaat. Bevat een voorbeeld van hoe deze timings als statsd-statistieken kunnen worden uitgezonden.

WSGIMiddleware

Een middleware-klasse die verantwoordelijk is voor het converteren van WSGI-applicaties naar ASGI-applicaties.