Skip to content

把 Python ASGI 應用搬進瀏覽器:Pyodide 加 Service Worker 的完整實作

原文連結:https://github.com/simonw/research/tree/main/pyodide-asgi-browser#readme

文章說明

這篇研究報告展示一個很有意思的實驗:把原本應該在伺服器端執行的 Python ASGI 應用,整個搬到瀏覽器裡。作者不只是做一個玩具 demo,而是用 FastAPI 與 Datasette 兩個例子,完整驗證導頁、表單、JSON API 與前端 fetch 都能在瀏覽器內完成。

內容介紹

核心概念是把 Python 應用放進 Pyodide 執行,然後讓 Service Worker 攔截同源下 /app/ 路徑的所有請求。當使用者點連結、送出表單,或頁面內 JavaScript 呼叫 fetch 時,Service Worker 會把這些 HTTP 請求序列化,再透過 shell page 與 MessageChannel 送到長生命週期的 Web Worker。該 worker 內部跑著 Pyodide 與 ASGI bridge,將請求轉成 ASGI scope,驅動 FastAPI 或 Datasette 回應,最後再把 response 傳回瀏覽器。

作者特別強調 shell page 與 iframe 的設計。因為如果直接把 app 本身當頁面載入,任何導頁都會讓 Python runtime 被替換掉;改成由外層 shell page 持有長生命週期的 Pyodide worker,再在 iframe 中顯示 /app/ 內容,就能在多次導頁中維持同一個 Python 執行環境。這也是為什麼整個機制能同時支援一般 HTML 導航、303 redirect,以及頁面內 fetch。

另一個很有價值的細節是 vendor 策略。作者發現某些沙箱環境下瀏覽器本身沒有外網,但宿主機有,因此不能依賴 CDN 動態拉 Pyodide 與套件。解法是用 vendor.py 預先把 Pyodide runtime 與必要 wheels 下載到本地 vendor 目錄,由 worker 在啟動時從本機資源載入。這讓整個 demo 可離線運作,也更容易重現。

文章不是只停在概念圖,還補上了完整測試策略。除了用純 Python 單元測試直接驅動 bridge 與 app,也用 Playwright 跑真實 Chromium,驗證導頁、POST 後 redirect、JSON API、Datasette 的資料存取與寫入流程都成立。結果是 27 個測試全部通過,證明這不是只在投影片上成立的架構。

對 Datasette 的處理尤其值得一看。由於它有 base_url、frame-busting headers 與部分硬編碼路徑等問題,作者在 bridge 層與 middleware 做了幾個針對性修補,例如重寫 /-/jump 路徑、剝除 iframe 相關阻擋 header、修正常見的 /app/app/ 雙重前綴問題。這些修補讓研究從單純 FastAPI demo 提升成一個更有普遍性的 ASGI-in-browser 實驗。

你可以帶走的重點

  1. 只要把 HTTP 請求轉成 ASGI 協定,許多 Python Web App 理論上都能在瀏覽器內執行。

  2. Service Worker、shell page、iframe 與 Web Worker 的分工,是這個方案可行的關鍵。

  3. 預先 vendor Pyodide 與 wheels,可以讓系統在受限網路環境中仍可運作。

  4. 真正困難的不只是在瀏覽器中跑 Python,而是處理既有框架的 URL、redirect、header 與生命週期細節。

  5. 如果要把這種概念推向實務,測試必須同時覆蓋 bridge 層與真實瀏覽器行為。

適合誰閱讀

  • 對瀏覽器端 Python、Pyodide 或 WASM 應用架構有興趣的工程師
  • 想研究如何把既有 ASGI 應用搬到 edge 或離線環境的開發者
  • 正在做原型驗證、教育 demo 或無後端應用實驗的技術團隊

由 Wo9Fei 製作