Lewati ke isi

Middleware

Starlette berisi beberapa kelas middleware untuk menambahkan perilaku yang berlaku untuk seluruh aplikasi. Ini diimplementasikan sebagai kelas middleware ASGI standar dan dapat diterapkan pada Starlette atau aplikasi ASGI lainnya.

Gunakan peralatan tengah

Kelas aplikasi Starlette memungkinkan Anda memasukkan middleware ASGI dengan cara yang memastikan selalu dibungkus oleh penangan pengecualian.

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware
Dari starlette.middleware.htpsredirect impor HTTPSRedirectMiddleware
Dari starlette.middleware.trustedhost impor TrustedHostMiddleware

Rute =...

# Pastikan bahwa semua permintaan termasuk example.com 'atau
# '*. example.com' host header, dan ketat menegakkan http-hanya akses.
Peralatan Tengah = [
    Peralatan Tengah (
        TrustedHostMiddleware,
        diperbolehkan _ hosts = ['example.com', '*. example.com'],
    ),
    Middleware (HTTPSRedirectMiddleware)
]

App = Starlette (rute = rute, middleware=middleware)

Secara default, setiap aplikasi Starlette secara otomatis mencakup dua buah middleware:

  • ServerErrorMiddleware- Memastikan bahwa pengecualian aplikasi dapat mengembalikan 500 halaman kustom, atau menampilkan informasi jejak untuk aplikasi dalam mode DEBUG. Ini selalu merupakan lapisan middleware terluar.
  • ExceptionMiddleware - Tambahkan penangan pengecualian sehingga jenis pengecualian tertentu yang diharapkan dapat dikaitkan dengan fungsi penangan. Misalnya, menaikkan dalam titik akhir HTTPException(status_code=404) Halaman 404 kustom pada akhirnya akan dirender.

Middleware dievaluasi dari atas ke bawah, jadi dalam aplikasi sampel kami, aliran eksekusi akan terlihat seperti ini:

  • peralatan tengah
    • ServerErrorMiddleware
    • TrustedHostMiddleware
    • HTTPSRedirectMiddleware
    • ExceptionMiddleware
  • rute
  • titik akhir

Implementasi middleware berikut tersedia dalam paket Starlette:

Peralatan Tengah CORS

Tambahkan header CORS yang sesuai ke respons keluar untuk memungkinkan permintaan lintas asal dari browser.

Parameter default yang digunakan oleh implementasi CORSMiddleware dibatasi secara default, jadi Anda harus secara eksplisit mengaktifkan sumber, metode, atau header tertentu sehingga browser diizinkan untuk menggunakannya dalam konteks lintas-asal.

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware
CORSMiddleware impor dari starlette.middleware.cors

Rute =...

Peralatan Tengah = [
    Middleware (CORSMiddleware, izinkan _ origins = ['*'])
]

App = Starlette (rute = rute, middleware=middleware)

Argumen berikut didukung:

  • allow_origins- Daftar sumber yang harus diizinkan untuk membuat permintaan lintas asal. Misalnya ['https://example.org', 'https://www.example.org']. Anda dapat menggunakan ['*'] untuk mengizinkan sumber apa pun.
  • allow_origin_regex- String ekspresi reguler yang cocok dengan sumber yang harus diizinkan untuk membuat permintaan lintas asal. Misalnya:'https://.*\.example\.org'.
  • allow_methods- Daftar metode HTTP yang harus diizinkan untuk permintaan lintas asal. Defaultnya adalah ['GET']. Anda dapat menggunakan ['*'] untuk mengizinkan semua metode standar.
  • allow_headers- Daftar header permintaan HTTP yang harus didukung untuk permintaan lintas asal. Default adalah []. Anda dapat menggunakan ['*'] untuk mengizinkan semua header. Untuk permintaan CORS, header Accept,Accept-Language,Content-Language, dan Content-Type selalu diperbolehkan.
  • allow_credentials- menunjukkan bahwa cookie untuk permintaan lintas asal harus didukung. Defaultnya adalah False. Juga, untuk memungkinkan kredensial,allow_origins,allow_methods, dan allow_headers tidak dapat diatur ke ['*'], semuanya harus ditentukan secara eksplisit.
  • expose_headers- Menunjukkan header respons apa pun yang harus dapat diakses ke browser. Default adalah [].
  • max_age- Mengatur waktu maksimum dalam hitungan detik bagi browser untuk menyimpan respons CORS. Defaultnya ke 600.

Middleware menanggapi dua jenis permintaan HTTP tertentu...

Berbagi Sumber Daya Lintas Asal (CORS) Periksa Permintaan

Ini adalah permintaan OPTIONS dengan header Origin dan Access-Control-Request-Method. Dalam hal ini, middleware akan mencegat permintaan masuk dan merespons dengan header CORS yang sesuai, serta respons 200 atau 400 untuk tujuan informasi.

permintaan sederhana

Setiap permintaan dengan header Origin. Dalam hal ini, middleware akan melewati permintaan seperti biasa, tetapi akan menyertakan header CORS yang sesuai sebagai tanggapan.

percakapan middleware

Tambahkan sesi HTTP berdasarkan cookie tanda tangan. Informasi sesi dapat dibaca tetapi tidak dapat dimodifikasi.

Gunakan antarmuka kamus request.session untuk mengakses atau memodifikasi data sesi.

Argumen berikut didukung:

  • secret_key - Harus string acak.
  • session_cookie Nilai default adalah "percakapan."
  • max_age- waktu kedaluwarsa sesi dalam hitungan detik. Default adalah 2 minggu. Jika disetel ke None, periode valid cookie akan sama dengan durasi sesi browser. Bendera* same_site- SameSite mencegah browser mengirim cookie sesi selama permintaan lintas situs. Defaultnya adalah 'lax'.
  • path- Path ditetapkan untuk cookie sesi. Default adalah '/'.
  • https_only - Menunjukkan bahwa bendera keamanan harus disetel (hanya untuk digunakan dengan HTTPS). Default adalah False ...
  • domain- Domain cookie digunakan untuk berbagi cookie di antara subdomain atau di seluruh domain. Peramban mengatur domain secara default ke host yang sama tempat cookie disetel, tidak termasuk subdomain (referensi).

    Starlette.application diimpor dari Starlette Impor Middleware dari starlette.middleware Dari starlette.middleware.session impor SessionMiddleware

    Rute =...

    Peralatan Tengah = [ Middleware (SessionMiddleware, secret _ key =..., https _ only = Benar) ]

    App = Starlette (rute = rute, middleware=middleware)

Alat tengah redirect HTTPS

Mandat bahwa semua permintaan yang masuk harus https atau wss. Setiap permintaan masuk ke http atau ws akan dialihkan ke skema keamanan.

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware
Dari starlette.middleware.htpsredirect impor HTTPSRedirectMiddleware

Rute =...

Peralatan Tengah = [
    Middleware (HTTPSRedirectMiddleware)
]

App = Starlette (rute = rute, middleware=middleware)

Tidak ada opsi konfigurasi untuk kelas middleware ini.

Middleware Tuan Rumah Terpercaya

Paksa semua permintaan yang masuk untuk memiliki set header Host yang benar untuk melindungi dari serangan header host HTTP.

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware
Dari starlette.middleware.trustedhost impor TrustedHostMiddleware

Rute =...

Peralatan Tengah = [
    Middleware (TrustedHostMiddleware, diperbolehkan _ hosts = ['example.com', '*. example.com'])
]

App = Starlette (rute = rute, middleware=middleware)

Argumen berikut didukung:

  • allowed_hosts- Daftar domain yang harus diizinkan sebagai nama host. Domain kartu liar seperti *.example.com dapat digunakan untuk mencocokkan subdomain. Untuk mengizinkan nama host apa pun, gunakan allowed_hosts=["*"] atau hilangkan middleware.
  • www_redirect- Jika diatur ke True, permintaan untuk versi non-www dari host yang diizinkan akan dialihkan ke rekan-rekan www mereka. Default adalah True.

Jika permintaan masuk tidak divalidasi dengan benar, tanggapan 400 akan di kirim.

Peralatan Tengah GZip

Menangani respon GZip untuk setiap permintaan yang mengandung "gzip" di header Accept-Encoding.

Middleware akan menangani respons standar dan streaming.

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware
Dari starlette.middleware.gzip impor GZipMiddleware


Rute =...

Peralatan Tengah = [
    Middleware (GZipMiddleware, minimum _ size = 1000, tingkat kompresi = 9)
]

App = Starlette (rute = rute, middleware=middleware)

Argumen berikut didukung:

  • minimum_size- Jangan GZip untuk tanggapan yang lebih kecil dari ukuran byte minimum ini. Defaultnya adalah 500.
  • compresslevel- digunakan selama kompresi GZip. Ini adalah bilangan bulat dari 1 hingga 9. Nilai default adalah 9. Semakin rendah nilainya, semakin cepat kompresannya, tetapi semakin besar ukuran file; semakin tinggi nilainya, semakin lambat kompresannya, tetapi semakin kecil ukuran file.

Middleware tidak merespons GZip yang telah mengatur Content-Encoding untuk mencegahnya dikodekan dua kali.

Perangkat tengah BaseHTTP

Kelas abstrak yang memungkinkan Anda menulis middleware ASGI untuk antarmuka permintaan / respons.

penggunaan

Untuk menerapkan kelas middleware menggunakan BaseHTTPMiddleware, Anda harus mengesampingkan metode async def dispatch(request, call_next).

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware
Dari starlette.middleware.base impor BaseHTTPMiddleware


Kelas CustomHeaderMiddleware (BaseHTTPMiddleware):
    Aync def pengiriman (sendiri, permintaan, panggilan _ berikutnya):
        Tanggapan = tunggu panggil _ next (permintaan)
        Tanggap.header ['Custom'] = 'Examplede'
        Respon kembali

Rute =...

Peralatan Tengah = [
    Middleware (CustomHeaderMiddleware)
]

App = Starlette (rute = rute, middleware=middleware)

Jika Anda ingin menyediakan opsi konfigurasi untuk kelas middleware, Anda harus mengesampingkan metode __init__ untuk memastikan bahwa argumen pertama adalah app dan argumen yang tersisa adalah argumen kata kunci opsional. Jika ya, pastikan untuk mengatur properti app pada instance.

Kelas CustomHeaderMiddleware (BaseHTTPMiddleware):
    Def _ _ init _ _ (diri, aplikasi, header _ value = 'Contoh'):
        Super (). _ _ init _ _ (aplikasi)
        Nilai _ header = nilai header

    Aync def pengiriman (sendiri, permintaan, panggilan _ berikutnya):
        Tanggapan = tunggu panggil _ next (permintaan)
        Tanggap.header ['Custom'] = self.header _ value
        Respon kembali


Peralatan Tengah = [
    Middleware (CustomHeaderMiddleware, header _ value = 'Disesuaikan')
]

App = Starlette (rute = rute, middleware=middleware)

Kelas Middleware tidak boleh mengubah status mereka di luar metode __init__. Sebagai gantinya, Anda harus meninggalkan negara bagian mana pun lokal dengan metode dispatch, atau meneruskannya secara eksplisit, daripada mengubah keadaan contoh middleware.

batasan

Saat ini,BaseHTTPMiddleware memiliki beberapa keterbatasan yang diketahui:

  • Menggunakan BaseHTTPMiddleware akan mencegah perubahan contextlib.ContextVar menyebar ke atas. Artinya, jika Anda menetapkan nilai untuk ContextVar di titik akhir dan mencoba membacanya dari estafet, Anda akan menemukan bahwa nilainya berbeda dari apa yang Anda tetapkan di titik akhir (lihat tes ini sebagai contoh perilaku ini).

Untuk mengatasi keterbatasan ini, gunakan middleware ASGI murni, seperti yang ditunjukkan di bawah ini.

Middleware ASGI Murni

Spesifikasi ASGI memungkinkan untuk mengimplementasikan middleware ASGI secara langsung menggunakan antarmuka ASGI, sebagai serangkaian aplikasi ASGI, memanggil yang berikutnya secara bergantian. Faktanya, ini adalah bagaimana kelas middleware yang di kirim dengan Starlette diimplementasikan.

Pendekatan tingkat yang lebih rendah ini memberikan kontrol yang lebih besar atas perilaku dan meningkatkan interoperabilitas di seluruh kerangka kerja dan server. Ini juga mengatasi keterbatasan BaseHTTPMiddleware.

Cara paling umum untuk membuat middleware ASGI adalah dengan menggunakan kelas.

Kelas ASGIMiddleware:
    Def _ _ init _ _ (diri, aplikasi):
        self.app = aplikasi

    Aync def _ _ call _ _ (diri, lingkup, terima, kirim):
        Menunggu self.app (lingkup, menerima, mengirim)

Middleware di atas adalah middleware ASGI paling dasar. Dibutuhkan aplikasi ASGI induk sebagai parameter dalam konstruktornya dan mengimplementasikan metode async __call__ yang memanggil aplikasi induk.

Beberapa implementasi, seperti asgi-cors, menggunakan gaya alternatif, menggunakan fungsi:

Alat fungsi impor

Pasti asgi _ middleware ():
    Def asgi _ dekorator (aplikasi):

        @ fungtools.wraps (aplikasi)
        Aync def dibungkus _ app (lingkup, terima, kirim):
            Await aplikasi (lingkup, terima, kirim)

        Kembali dibungkus _ app

    Kembali asgi _ dekorator

Namun, middleware ASGI harus menjadi objek yang dapat dipanggil yang menerima tiga parameter:scope,receive, dan send.

  • scope adalah kamus yang menyimpan informasi tentang koneksi, di mana scope["type"] mungkin:
    • "http" : Digunakan untuk permintaan HTTP.
    • "websocket" : Digunakan untuk koneksi WebSocket.
    • "lifespan" : Untuk pesan siklus hidup ASGI.
  • receive dan send Dapat digunakan untuk bertukar pesan acara ASGI dengan server ASGI - lebih lanjut tentang ini di bawah ini. Jenis dan konten pesan ini tergantung pada jenis ruang lingkup. Pelajari lebih lanjut dalam spesifikasi ASGI.

Menggunakan middleware ASGI murni

Middleware ASGI murni dapat digunakan sama seperti middleware lainnya.

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware

Dari middleware impor ASGIMiddleware

Rute =...

Peralatan Tengah = [
    Middleware (ASGIMiddleware),
]

Aplikasi = Starlette (..., middleware=middleware)

Lihat juga Menggunakan Middleware.

ketik anotasi

Ada dua cara untuk memberi anotasi pada middleware: menggunakan Starlette itu sendiri atau asgiref.

  • Menggunakan Starlette: Cocok untuk kasus penggunaan yang paling umum.

    Dari starlette.type import ASGIApp, Pesan, Lingkup, Terima, Kirim

    Kelas ASGIMiddleware: Def _ _ init _ _ (sendiri, aplikasi: ASGIApp) - > Tidak ada: self.app = aplikasi

    Sinkronisasi def _ _ call _ _ (diri, lingkup: Lingkup, terima: Terima, kirim: Kirim) - > Tidak ada:
        Jika lingkup ["ketik"]! = "http":
            Kembali menunggu self.app (lingkup, menerima, mengirim)
    
        Async def send _ wrapper (pesan: Pesan) - > Tidak ada:
            #... Lakukan sesuatu
            Menunggu pengiriman (pesan)
    
        Menunggu self.app (lingkup, menerima, mengirim _ wrapper)
    
  • menggunakan asgiref: untuk mencapai petunjuk tipe yang lebih ketat.

    Dari asgiref.typing import ASGI3Application, ASGIReceiveCallable, ASGISendCallable, Scope Dari asgiref.typing impor ASGIReceiveEvent, ASGISendEvent

    Kelas ASGIMiddleware: Def _ _ init _ _ (sendiri, aplikasi: ASGI3Aplikasi) - > Tidak ada: self.app = aplikasi

    Async def _ _ call _ _ (diri, lingkup: Lingkup, terima: ASGIReceiveCallable, kirim: ASGISendCallable) - > Tidak ada:
        Jika lingkup ["ketik"]! = "http":
            Menunggu self.app (lingkup, menerima, mengirim)
            Kembali
    
        Async def send _ wrapper (pesan: ASGISendEvent) - > Tidak ada:
            #... Lakukan sesuatu
            Menunggu pengiriman (pesan)
    
        Kembali menunggu self.app (lingkup, terima, kirim _ wrapper)
    

pola umum

Hanya menangani permintaan tertentu

Middleware ASGI dapat menerapkan perilaku tertentu berdasarkan isi scope.

Misalnya, untuk hanya menangani permintaan HTTP, tulis...

Kelas ASGIMiddleware:
    Def _ _ init _ _ (diri, aplikasi):
        self.app = aplikasi

    Aync def _ _ call _ _ (diri, lingkup, terima, kirim):
        Jika lingkup ["ketik"]! = "http":
            Menunggu self.app (lingkup, menerima, mengirim)
            Kembali

        # Lakukan sesuatu di sini!

        Menunggu self.app (lingkup, menerima, mengirim)

Demikian pula, hanya WebSocket middleware yang dilindungi pada scope["type"] != "websocket".

Middleware mungkin juga berperilaku berbeda tergantung pada metode permintaan, URL, header, dan sebagainya.

Komponen Starlette Multiplexing

Starlette menyediakan beberapa struktur data yang menerima parameter ASGI scope,receive, dan / atau send, memungkinkan Anda untuk bekerja pada tingkat abstraksi yang lebih tinggi. Struktur data tersebut termasuk Request,Headers,QueryParams,URL, dan sebagainya.

Misalnya, Anda dapat membuat instansiasi Request untuk memeriksa permintaan HTTP dengan lebih mudah.

Dari bintang. permintaan permintaan impor

Kelas ASGIMiddleware:
    Def _ _ init _ _ (diri, aplikasi):
        self.app = aplikasi

    Aync def _ _ call _ _ (diri, lingkup, terima, kirim):
        Jika lingkup ["ketik"] = = "http":
            Permintaan = Permintaan (ruang lingkup)
            ... # Gunakan `request.method`,`request.url`,`request.headers`, dll.

        Menunggu self.app (lingkup, menerima, mengirim)

Anda juga dapat menggunakan kembali tanggapan, yang juga merupakan aplikasi ASGI.

Kirim tanggapan yang bersemangat

Periksa koneksi scope Memungkinkan Anda untuk memanggil aplikasi ASGI yang berbeda secara bersyarat. Satu kasus penggunaan mungkin untuk mengirim tanggapan tanpa menelepon aplikasi.

Misalnya, middleware ini menggunakan kamus untuk melakukan pengalihan permanen berdasarkan jalur yang diminta. Jika Anda perlu merefactor pola URL perutean, ini dapat digunakan untuk menerapkan dukungan berkelanjutan untuk URL lama.

URL diimpor dari starlette.datastructures
Tanggapan langsung diimpor dari starlette.responses

Pengalihan Kelas Middleware:
    Def _ _ init _ _ (diri, aplikasi, path _ mapping: dict):
        self.app = aplikasi
        Self.path _ mapping = path _ mapping

    Aync def _ _ call _ _ (diri, lingkup, terima, kirim):
        Jika lingkup ["ketik"]! = "http":
            Menunggu self.app (lingkup, menerima, mengirim)
            Kembali

        URL = URL (ruang lingkup = ruang lingkup)

        Jika url.path dalam diri sendiri. path _ mapping:
            Url = url.ganti (path = self.path _ mapping [url.path])
            Response = RedirectResponse (url, status _ code = 301)
            Menunggu tanggapan (lingkup, menerima, mengirim)
            Kembali

        Menunggu self.app (lingkup, menerima, mengirim)

Contoh penggunaan akan terlihat seperti ini:

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware

Rute =...

Pengalihan = {
    "/ v1 / sumber daya /": "/ v2 / sumber daya /,"
    # ...
}

Peralatan Tengah = [
    Middleware (RedirectsMiddleware, path _ mapping=redirections),
]

App = Starlette (rute = rute, middleware=middleware)

Periksa atau Ubah Permintaan

Informasi permintaan dapat diakses atau diubah dengan mengoperasikan scope. Untuk contoh lengkap pola ini, lihat ProxyHeadersMiddleware Uvicorn, yang memeriksa dan menyesuaikan scope saat disajikan di belakang proxy front-end.

Selain itu, membungkus objek yang dapat dipanggil receive ASGI memungkinkan Anda mengakses atau memodifikasi badan permintaan HTTP dengan memanipulasi pesan acara http.request ASGI.

Misalnya, middleware ini menghitung dan mencatat ukuran badan permintaan masuk...

Kelas LoggedRequestBodySizeMiddleware:
    Def _ _ init _ _ (diri, aplikasi):
        self.app = aplikasi

    Aync def _ _ call _ _ (diri, lingkup, terima, kirim):
        Jika lingkup ["ketik"]! = "http":
            Menunggu self.app (lingkup, menerima, mengirim)
            Kembali

        tubuh _ size = 0

        Async def menerima _ logging _ request _ body _ size ():
            Ukuran tubuh bukan lokal

            Pesan = menunggu menerima ()
            Tegaskan pesan ["ketik"] = = "permintaan htp."

            tubuh _ size + = len (message.get ("tubuh," b "))

            Jika tidak pesan.get ("more _ body," Salah):
                Cetak (f "Ukuran badan permintaan adalah: byte{body_size}")

            Pesan kembali

        Menunggu self.app (scope, receives _ logging _ request _ body _ size, send)

Demikian pula, middleware WebSocket dapat memanipulasi pesan acara websocket.receive ASGI untuk memeriksa atau mengubah data WebSocket yang masuk.

Untuk contoh mengubah isi permintaan HTTP, lihat msgpack-asgi.

Periksa atau ubah tanggapannya

Membungkus send ASGI callable memungkinkan Anda untuk memeriksa atau memodifikasi respon HTTP yang di kirim oleh aplikasi yang mendasarinya. Untuk melakukan ini, bereaksi terhadap pesan peristiwa http.response.start atau http.response.body ASGI.

Misalnya, middleware ini menambahkan beberapa header respons tambahan tetap:

MutableHeader mengimpor dari starlette.datastructures

Kelas ExtraResponseHeadersMiddleware:
    Def _ _ init _ _ (diri, aplikasi, header):
        self.app = aplikasi
        Diri = header

    Aync def _ _ call _ _ (diri, lingkup, terima, kirim):
        Jika lingkup ["ketik"]! = "http":
            Kembali menunggu self.app (lingkup, menerima, mengirim)

        Sinkronisasi def send _ with _ extra _ headers (pesan):
            Jika pesan ["ketik"] = = "htp.response.start":
                Header = MutableHeader (scope = pesan)
                Untuk kunci, nilai dalam self.headers:
                    Headers.append (kunci, nilai)

            Menunggu pengiriman (pesan)

        Menunggu self.app (lingkup, menerima, mengirim _ dengan _ extra _ header)

Lihat juga asgi-logger, yang memiliki contoh memeriksa tanggapan HTTP dan mencatat saluran log akses HTTP yang dapat dikonfigurasi.

Demikian pula, WebSocket middleware dapat memanipulasi pesan acara websocket.send ASGI untuk memeriksa atau mengubah data WebSocket yang keluar.

Harap dicatat bahwa jika Anda mengubah tubuh respons, Anda perlu memperbarui header Content-Length respons agar sesuai dengan panjang tubuh respons baru. Lihat brotli-asgi untuk contoh lengkap.

Berikan informasi ke titik akhir

Jika Anda perlu berbagi informasi dengan aplikasi atau titik akhir yang mendasarinya, Anda dapat menyimpannya di kamus scope. Perhatikan bahwa ini adalah konvensi - misalnya, Starlette menggunakan metode ini untuk berbagi informasi perutean dengan titik akhir - tetapi ini bukan bagian dari spesifikasi ASGI. Jika ya, pastikan untuk menggunakan kunci yang tidak mungkin digunakan oleh middleware atau aplikasi lain untuk menghindari konflik.

Misalnya, titik akhir akan dapat mengakses request.scope["asgi_transaction_id"] ketika middleware berikut disertakan.

Impor uid

Kelas TransactionIDMiddleware:
    Def _ _ init _ _ (diri, aplikasi):
        self.app = aplikasi

    Aync def _ _ call _ _ (diri, lingkup, terima, kirim):
        Cakupan ["asgi _ transaksi _ id"] = uuid.uuid4 ()
        Menunggu self.app (lingkup, menerima, mengirim)

Pembersihan dan penanganan kesalahan

Anda dapat membungkus aplikasi Anda dalam blok try/except/finally atau manajer konteks untuk pembersihan atau penanganan kesalahan.

Misalnya, middleware berikut mungkin mengumpulkan metrik dan menangani pengecualian aplikasi...

Waktu impor

Kelas MonitoringMiddleware:
    Def _ _ init _ _ (diri, aplikasi):
        self.app = aplikasi

    Aync def _ _ call _ _ (diri, lingkup, terima, kirim):
        Mulai = waktu ()
        Coba:
            Menunggu self.app (lingkup, menerima, mengirim)
        Kecua*******ecualian sebagai exc:
            ... # Proses pengecualian
            Naikkan
        Akhirnya:
            Akhir = waktu. Waktu ()
            Elapsed = akhir - mulai
            ... # Kirimkan `elapsed` sebagai metrik ke backend pemantauan

Silakan juga merujuk ke timing-asgi untuk contoh lengkap dari pola ini.

Masalah potensial

Middleware ASGI harus tanpa kewarganegaraan

Karena ASGI dirancang untuk menangani permintaan bersamaan, setiap status khusus koneksi harus dibatasi pada __call__ Dalam lingkup implementasi. Kegagalan untuk melakukannya biasanya menghasilkan tabrakan baca / tulis variabel di seluruh permintaan, dan kesalahan mungkin terjadi.

Misalnya, jika header X-Mock hadir dalam respons, operasi ini secara kondisional menggantikan tubuh respons...

✅ melakukan

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

❌ Tidak.

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

Silakan juga merujuk ke GZipMiddleware untuk contoh implementasi lengkap yang membahas masalah potensial ini.

Pembacaan lebih lanjut

Dokumen ini harus cukup sebagai dasar yang baik untuk membuat middleware ASGI.

Namun, ada banyak artikel bagus tentang topik ini:

Menggunakan middleware dalam kerangka kerja lain

Untuk membungkus middleware ASGI di sekitar aplikasi ASGI lainnya, Anda harus menggunakan pola yang lebih umum untuk membungkus instance aplikasi:

Aplikasi = TrustedHostMiddleware (aplikasi, diizinkan _ host = ['example.com'])

Anda juga dapat melakukannya dengan contoh aplikasi Starlette, tetapi lebih baik menggunakan gaya middleware=<List of Middleware instances> karena akan:

  • memastikan semuanya terbungkus dalam lapisan terluar ServerErrorMiddleware.
  • mempertahankan contoh app tingkat atas.

Menerapkan middleware ke grup perutean

Middleware juga dapat ditambahkan ke contoh Mount, yang memungkinkan Anda untuk menerapkan middleware ke satu set perutean atau sub-aplikasi.

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware
Dari starlette.middleware.gzip impor GZipMiddleware
Dari bintang. rute impor Gunung, Rute


Rute = [
    Gunung (
        "/",
        Rute = [
            Rute (
                "/ contoh,"
                Endpoint =...,
            )
        ],
        Middleware = [Middleware (GZipMiddleware) ]
    )
]

Aplikasi = Starlette (rute = rute)

Harap dicatat bahwa middleware yang digunakan dengan cara ini tidak dibungkus dengan pengecualian menangani middleware seperti middleware yang diterapkan pada aplikasi Starlette. Ini biasanya tidak menjadi masalah karena hanya berfungsi untuk memeriksa atau memodifikasi middleware untuk Response, dan itupun Anda mungkin tidak ingin menerapkan logika ini pada respons kesalahan. Jika Anda ingin menerapkan logika middleware ke respons kesalahan hanya pada rute tertentu, ada beberapa opsi:

  • Tambahkan ExceptionMiddleware pada Mount
  • menambahkan blok try/except ke middleware Anda dan mengembalikan respons kesalahan dari sana
  • membagi penandaan dan pemrosesan menjadi dua middleware, satu di tempatkan pada Mount yang menandai respons yang memerlukan pemrosesan (misalnya dengan mengatur scope["log-response"] = True), dan yang lainnya diterapkan pada aplikasi Starlette untuk melakukan angkat berat.

Kelas Route/WebSocket juga menerima parameter middleware yang memungkinkan Anda menerapkan middleware ke satu rute:

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware
Dari starlette.middleware.gzip impor GZipMiddleware
Dari starlette. rute impor routing


Rute = [
    Rute (
        "/ contoh,"
        Endpoint =...,
        Middleware = [Middleware (GZipMiddleware) ]
    )
]

Aplikasi = Starlette (rute = rute)

Anda juga dapat menerapkan middleware ke kelas Router, yang memungkinkan Anda menerapkan middleware ke serangkaian rute:

Starlette.application diimpor dari Starlette
Impor Middleware dari starlette.middleware
Dari starlette.middleware.gzip impor GZipMiddleware
Dari bintang. rute impor Rute, Router


Rute = [
    Rute ("/ contoh," titik akhir =...),
    Rute ("/ lain," titik akhir =...),
]

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

middleware pihak ketiga

Asgi-auth-github

Middleware ini menambahkan otentikasi ke aplikasi ASGI apa pun, yang mengharuskan pengguna untuk masuk dengan akun GitHub mereka (melalui OAuth). Akses dapat dibatasi untuk pengguna tertentu atau anggota organisasi atau tim GitHub tertentu.

agi csrf

Middleware untuk perlindungan terhadap serangan CSRF. Middleware ini mengimplementasikan mode cookie berkomitmen ganda di mana cookie disetel dan kemudian dibandingkan dengan bidang bentuk tersembunyi csrftoken atau header HTTP x-csrftoken.

Peralatan Middleware Otenlib

Alternatif langsung untuk middleware sesi Starlette, menggunakan modul JWT Authlib.

alat tengah Bugsnag

Kelas middleware untuk logging pengecualian untuk Bugsnag.

Peralatan Tengah CSRF

Middleware untuk melindungi dari serangan CSRF. Middleware ini mengimplementasikan mode cookie berkomitmen ganda di mana cookie disetel dan kemudian dibandingkan dengan header HTTP x-csrftoken.

Peralatan tengah data awal

Middleware dan dekorator untuk mendeteksi dan menolak permintaan data TLSv1.3 sebelumnya.

Peralatan tengah prometheus

Kelas middleware untuk menangkap metrik Prometheus terkait dengan permintaan dan tanggapan, termasuk permintaan, waktu, dll...

proxy header middleware

Uvicorn menyertakan kelas middleware untuk menentukan alamat IP sisi klien berdasarkan header X-Forwarded-Proto dan X-Forwarded-For saat menggunakan server proxy. Untuk konfigurasi proxy yang lebih kompleks, Anda mungkin perlu menyesuaikan middleware ini.

nilai pembatas middleware

Middleware yang membatasi tarif. URL pencocokan ekspresi reguler; aturan fleksibel; sangat dapat disesuaikan. Sangat mudah digunakan.

Persyaratan Middleware

Kelas middleware untuk membaca / menghasilkan ID permintaan dan melampirkannya ke log aplikasi.

Peralatan tengah Rollbar

Kelas middleware untuk mencatat pengecualian, kesalahan, dan pesan log ke Rollbar.

StarletteOpentracing

Kelas middleware yang mengirimkan informasi pelacakan ke pelacak yang kompatibel OpenTracing.io dan dapat digunakan untuk menganalisis dan memantau aplikasi terdistribusi.

Peralatan Tengah Kue Keamanan

Middleware yang dapat disesuaikan untuk menambahkan enkripsi dan dekripsi cookie otomatis ke aplikasi Starlette, memberikan dukungan tambahan untuk middleware berbasis cookie yang ada.

pengaturan waktu middleware

Kelas middleware yang memancarkan informasi waktu (waktu CPU dan waktu jam dinding) untuk setiap permintaan yang melewatinya. Termasuk contoh bagaimana memancarkan waktu ini sebagai metrik statistik.

WSGIMiddleware

Kelas middleware yang bertanggung jawab untuk mengonversi aplikasi WSGI ke aplikasi ASGI.