mirror of https://github.com/tiangolo/fastapi.git
🌐 Update translations for zh-hant (add-missing) (#15176)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
1122d37676
commit
df4417efa5
|
|
@ -0,0 +1,63 @@
|
|||
# 使用 Base64 表示位元組的 JSON { #json-with-bytes-as-base64 }
|
||||
|
||||
如果你的應用需要收發 JSON 資料,但其中需要包含二進位資料,你可以將它以 base64 編碼。
|
||||
|
||||
## Base64 與檔案 { #base64-vs-files }
|
||||
|
||||
請先考慮是否能用 [請求檔案](../tutorial/request-files.md) 來上傳二進位資料,並用 [自訂回應 - FileResponse](./custom-response.md#fileresponse--fileresponse-) 來傳送二進位資料,而不是把它們編碼進 JSON。
|
||||
|
||||
JSON 只能包含 UTF-8 編碼的字串,因此無法直接包含原始位元組。
|
||||
|
||||
Base64 可以把二進位資料編碼成字串,但為此會使用比原始二進位資料更多的字元,因此通常比直接使用檔案來得沒那麼有效率。
|
||||
|
||||
只有在確實必須把二進位資料包含在 JSON 裡,且無法改用檔案時,才使用 base64。
|
||||
|
||||
## Pydantic `bytes` { #pydantic-bytes }
|
||||
|
||||
你可以宣告含有 `bytes` 欄位的 Pydantic 模型,並在模型設定中使用 `val_json_bytes`,使其在驗證輸入的 JSON 資料時使用 base64;在驗證過程中,它會將 base64 字串解碼為位元組。
|
||||
|
||||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
|
||||
|
||||
如果你查看 `/docs`,會看到欄位 `data` 需要 base64 編碼的位元組:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/json-base64-bytes/image01.png">
|
||||
</div>
|
||||
|
||||
你可以發送如下的請求:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Some data",
|
||||
"data": "aGVsbG8="
|
||||
}
|
||||
```
|
||||
|
||||
/// tip
|
||||
|
||||
`aGVsbG8=` 是 `hello` 的 base64 編碼。
|
||||
|
||||
///
|
||||
|
||||
接著 Pydantic 會將該 base64 字串解碼,並在模型的 `data` 欄位中提供原始位元組。
|
||||
|
||||
你會收到類似以下的回應:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Some data",
|
||||
"content": "hello"
|
||||
}
|
||||
```
|
||||
|
||||
## Pydantic `bytes` 用於輸出資料 { #pydantic-bytes-for-output-data }
|
||||
|
||||
你也可以在模型設定中搭配 `ser_json_bytes` 使用 `bytes` 欄位來處理輸出資料;當產生 JSON 回應時,Pydantic 會將位元組以 base64 進行序列化。
|
||||
|
||||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
|
||||
|
||||
## Pydantic `bytes` 用於輸入與輸出資料 { #pydantic-bytes-for-input-and-output-data }
|
||||
|
||||
當然,你也可以使用同一個以 base64 設定的模型,同時處理輸入(以 `val_json_bytes` 驗證)與輸出(以 `ser_json_bytes` 序列化)的 JSON 資料。
|
||||
|
||||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
# 串流資料 { #stream-data }
|
||||
|
||||
如果你要串流可用 JSON 結構化的資料,應該[串流 JSON Lines](../tutorial/stream-json-lines.md)。
|
||||
|
||||
但如果你想串流純二進位資料或字串,以下是做法。
|
||||
|
||||
/// info
|
||||
|
||||
已在 FastAPI 0.134.0 新增。
|
||||
|
||||
///
|
||||
|
||||
## 使用情境 { #use-cases }
|
||||
|
||||
當你想串流純字串時可以用這個機制,例如直接轉發來自 AI LLM 服務的輸出。
|
||||
|
||||
你也可以用它來串流大型二進位檔案,邊讀邊將每個區塊(chunk)串流出去,而不必一次把整個檔案載入記憶體。
|
||||
|
||||
你也可以用同樣方式串流視訊或音訊,甚至可以在處理的同時即時產生並傳送。
|
||||
|
||||
## 使用 `yield` 的 `StreamingResponse` { #a-streamingresponse-with-yield }
|
||||
|
||||
如果在你的路徑操作函式中宣告 `response_class=StreamingResponse`,就可以用 `yield` 逐一送出每個資料區塊。
|
||||
|
||||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
|
||||
|
||||
FastAPI 會如實將每個資料區塊交給 `StreamingResponse`,不會嘗試將其轉換為 JSON 或其他格式。
|
||||
|
||||
### 非 async 路徑操作函式 { #non-async-path-operation-functions }
|
||||
|
||||
你也可以使用一般的 `def` 函式(沒有 `async`),並以相同方式使用 `yield`。
|
||||
|
||||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
|
||||
|
||||
### 不需要型別註解 { #no-annotation }
|
||||
|
||||
對於串流二進位資料,其實不需要宣告回傳型別註解。
|
||||
|
||||
由於 FastAPI 不會試圖用 Pydantic 將資料轉成 JSON,或以其他方式序列化,在這種情況下,型別註解僅供編輯器與工具使用,FastAPI 並不會用到它。
|
||||
|
||||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
|
||||
|
||||
這也意味著使用 `StreamingResponse` 時,你擁有自由與責任,需依需求自行產生並編碼要傳送的位元組資料,與型別註解無關。 🤓
|
||||
|
||||
### 串流位元組 { #stream-bytes }
|
||||
|
||||
一個主要用例是串流 `bytes` 而非字串,當然可以這麼做。
|
||||
|
||||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
|
||||
|
||||
## 自訂 `PNGStreamingResponse` { #a-custom-pngstreamingresponse }
|
||||
|
||||
在上述範例中,雖然串流了資料位元組,但回應沒有 `Content-Type` 標頭,因此用戶端不知道接收到的是哪種資料型別。
|
||||
|
||||
你可以建立 `StreamingResponse` 的自訂子類別,將 `Content-Type` 標頭設定為你要串流的資料型別。
|
||||
|
||||
例如,你可以建立 `PNGStreamingResponse`,透過 `media_type` 屬性把 `Content-Type` 設為 `image/png`:
|
||||
|
||||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
|
||||
|
||||
接著在路徑操作函式中用 `response_class=PNGStreamingResponse` 使用這個新類別:
|
||||
|
||||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
|
||||
|
||||
### 模擬檔案 { #simulate-a-file }
|
||||
|
||||
此範例中我們用 `io.BytesIO` 模擬檔案。它是只存在於記憶體中的類檔案物件,但提供相同的介面。
|
||||
|
||||
例如,我們可以像讀取一般檔案一樣,透過迭代來消耗其內容。
|
||||
|
||||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
|
||||
|
||||
/// note | 技術細節
|
||||
|
||||
另外兩個變數 `image_base64` 與 `binary_image`,分別是先將影像以 Base64 編碼,接著轉成位元組,最後再傳給 `io.BytesIO`。
|
||||
|
||||
這只是為了讓範例能放在同一個檔案中,方便你直接複製並執行。 🥚
|
||||
|
||||
///
|
||||
|
||||
使用 `with` 區塊可確保在產生器函式(包含 `yield` 的函式)完成後關閉該類檔案物件,也就是在送完回應之後。
|
||||
|
||||
在這個範例中因為是存在記憶體的假檔案(`io.BytesIO`),影響不大;但若是實際檔案,務必在處理完成後關閉檔案。
|
||||
|
||||
### 檔案與 Async { #files-and-async }
|
||||
|
||||
多數情況下,類檔案物件預設不相容於 async/await。
|
||||
|
||||
例如,它們沒有 `await file.read()`,也不支援 `async for chunk in file`。
|
||||
|
||||
而且在許多情況下,讀取它們會是阻塞操作(可能阻塞事件迴圈),因為資料是從磁碟或網路讀取。
|
||||
|
||||
/// info
|
||||
|
||||
上面的範例其實是例外,因為 `io.BytesIO` 物件已在記憶體中,讀取不會阻塞任何東西。
|
||||
|
||||
但在多數情況下,讀取檔案或類檔案物件會造成阻塞。
|
||||
|
||||
///
|
||||
|
||||
為了避免阻塞事件迴圈,你可以將路徑操作函式宣告為一般的 `def`(而非 `async def`),這樣 FastAPI 會在 threadpool worker 上執行它,避免阻塞主事件迴圈。
|
||||
|
||||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
|
||||
|
||||
/// tip
|
||||
|
||||
如果你需要在 async 函式內呼叫阻塞程式碼,或在阻塞函式中呼叫 async 函式,你可以使用 [Asyncer](https://asyncer.tiangolo.com),它是 FastAPI 的姊妹函式庫。
|
||||
|
||||
///
|
||||
|
||||
### `yield from` { #yield-from }
|
||||
|
||||
當你在迭代某個物件(如類檔案物件),並對每個項目使用 `yield` 時,也可以用 `yield from` 直接逐項產出,省略 `for` 迴圈。
|
||||
|
||||
這不是 FastAPI 特有的功能,而是 Python 語法;不過這招很實用。 😎
|
||||
|
||||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# 嚴格的 Content-Type 檢查 { #strict-content-type-checking }
|
||||
|
||||
預設情況下,FastAPI 會對 JSON 請求主體使用嚴格的 `Content-Type` 標頭檢查。也就是說,JSON 請求必須包含有效的 `Content-Type` 標頭(例如 `application/json`),請求主體(body)才能被解析為 JSON。
|
||||
|
||||
## CSRF 風險 { #csrf-risk }
|
||||
|
||||
這個預設行為在某個非常特定的情境下,能對一類跨站請求偽造(CSRF, Cross-Site Request Forgery)攻擊提供保護。
|
||||
|
||||
這類攻擊利用了瀏覽器在以下情況下允許腳本發送請求而不進行任何 CORS 預檢(preflight)檢查的事實:
|
||||
|
||||
- 沒有 `Content-Type` 標頭(例如以 `fetch()` 並使用 `Blob` 作為 body)
|
||||
- 且沒有送出任何身分驗證憑證
|
||||
|
||||
這種攻擊主要與以下情境相關:
|
||||
|
||||
- 應用在本機(例如 `localhost`)或內部網路中執行
|
||||
- 並且應用沒有任何身分驗證,假設同一個網路中的任何請求都可被信任
|
||||
|
||||
## 攻擊範例 { #example-attack }
|
||||
|
||||
假設你打造了一個在本機執行 AI 代理(AI agent)的方法。
|
||||
|
||||
它提供一個 API:
|
||||
|
||||
```
|
||||
http://localhost:8000/v1/agents/multivac
|
||||
```
|
||||
|
||||
同時也有一個前端:
|
||||
|
||||
```
|
||||
http://localhost:8000
|
||||
```
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
請注意兩者的主機(host)相同。
|
||||
|
||||
///
|
||||
|
||||
接著你可以透過前端讓 AI 代理代你執行動作。
|
||||
|
||||
由於它在本機執行、而非公開的網際網路上,你決定不設定任何身分驗證,只信任對本機網路的存取。
|
||||
|
||||
然後你的某位使用者可能安裝並在本機執行它。
|
||||
|
||||
接著他可能打開一個惡意網站,例如:
|
||||
|
||||
```
|
||||
https://evilhackers.example.com
|
||||
```
|
||||
|
||||
該惡意網站會使用 `fetch()` 並以 `Blob` 作為 body,向本機的 API 發送請求:
|
||||
|
||||
```
|
||||
http://localhost:8000/v1/agents/multivac
|
||||
```
|
||||
|
||||
即使惡意網站與本機應用的主機不同,瀏覽器也不會觸發 CORS 預檢請求,因為:
|
||||
|
||||
- 它在未經任何身分驗證的情況下執行,不需要送出任何憑證。
|
||||
- 由於缺少 `Content-Type` 標頭,瀏覽器認為它並未傳送 JSON。
|
||||
|
||||
接著,惡意網站就能讓本機的 AI 代理替使用者向前老闆發飆傳訊... 或做更糟的事。😅
|
||||
|
||||
## 公開的網際網路 { #open-internet }
|
||||
|
||||
如果你的應用部署在公開的網際網路上,你不會「信任網路」而允許任何人在未經身分驗證的情況下發送具權限的請求。
|
||||
|
||||
攻擊者可以直接執行腳本向你的 API 發送請求,無需透過瀏覽器互動,因此你多半已經對任何具權限的端點做了防護。
|
||||
|
||||
在這種情況下,這種攻擊/風險不適用於你。
|
||||
|
||||
此風險與攻擊主要與應用只在本機或內部網路上執行,且「僅依賴此為保護」的情境相關。
|
||||
|
||||
## 允許沒有 Content-Type 的請求 { #allowing-requests-without-content-type }
|
||||
|
||||
若你需要支援未送出 `Content-Type` 標頭的客戶端,可以將 `strict_content_type=False` 以停用嚴格檢查:
|
||||
|
||||
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
|
||||
|
||||
啟用此設定後,缺少 `Content-Type` 標頭的請求會將其主體解析為 JSON,這與舊版 FastAPI 的行為相同。
|
||||
|
||||
/// info | 資訊
|
||||
|
||||
此行為與設定新增於 FastAPI 0.132.0。
|
||||
|
||||
///
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# 編輯器支援 { #editor-support }
|
||||
|
||||
官方的 [FastAPI 擴充套件](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) 透過支援路徑操作(path operation)探索、導覽,以及 FastAPI Cloud 部署與即時日誌串流,強化你的 FastAPI 開發流程。
|
||||
|
||||
想了解更多關於此擴充套件的細節,請參考其 [GitHub 儲存庫](https://github.com/fastapi/fastapi-vscode) 中的 README。
|
||||
|
||||
## 安裝與設定 { #setup-and-installation }
|
||||
|
||||
**FastAPI 擴充套件** 同時提供給 [VS Code](https://code.visualstudio.com/) 與 [Cursor](https://www.cursor.com/)。你可以在各編輯器的擴充套件面板中直接安裝:搜尋「FastAPI」,並選擇由 **FastAPI Labs** 發佈的擴充套件。此擴充套件同樣可在瀏覽器版編輯器(如 [vscode.dev](https://vscode.dev) 與 [github.dev](https://github.dev))中使用。
|
||||
|
||||
### 應用程式探索 { #application-discovery }
|
||||
|
||||
預設情況下,擴充套件會自動在你的工作區中,掃描會實例化 `FastAPI()` 的檔案,以發現 FastAPI 應用程式。若自動偵測無法因應你的專案結構,你可以在 `pyproject.toml` 的 `[tool.fastapi]` 中,或在 VS Code 設定的 `fastapi.entryPoint` 中,使用模組標記法(例如 `myapp.main:app`)指定入口點。
|
||||
|
||||
## 功能 { #features }
|
||||
|
||||
- **Path Operation Explorer** - 顯示應用程式中所有 <dfn title="路由、端點">*路徑操作*</dfn> 的側邊欄樹狀檢視。點擊即可跳至任一路由或 router 定義。
|
||||
- **Route Search** - 使用 <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd>(macOS:<kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd>)依路徑、方法或名稱進行搜尋。
|
||||
- **CodeLens Navigation** - 在測試用 client 呼叫(例如 `client.get('/items')`)上方提供可點連結,一鍵跳至對應的路徑操作,讓你在測試與實作間快速切換。
|
||||
- **Deploy to FastAPI Cloud** - 一鍵將你的應用程式部署到 [FastAPI Cloud](https://fastapicloud.com/)。
|
||||
- **Stream Application Logs** - 從部署於 FastAPI Cloud 的應用程式即時串流日誌,並支援層級篩選與文字搜尋。
|
||||
|
||||
若你想熟悉此擴充套件的功能,可開啟命令面板(<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>;macOS:<kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>),選擇 "Welcome: Open walkthrough...",然後挑選 "Get started with FastAPI" walkthrough。
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
# Server-Sent Events(SSE) { #server-sent-events-sse }
|
||||
|
||||
你可以使用 Server-Sent Events(SSE)把資料串流傳送給用戶端。
|
||||
|
||||
這與[串流 JSON Lines](stream-json-lines.md)類似,但使用瀏覽器原生支援、透過 [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) 的 `text/event-stream` 格式。
|
||||
|
||||
/// info
|
||||
|
||||
在 FastAPI 0.135.0 新增。
|
||||
|
||||
///
|
||||
|
||||
## 什麼是 Server-Sent Events? { #what-are-server-sent-events }
|
||||
|
||||
SSE 是一種透過 HTTP 從伺服器向用戶端串流傳送資料的標準。
|
||||
|
||||
每個事件都是一個小型文字區塊,包含 `data`、`event`、`id` 和 `retry` 等「欄位」,並以空白行分隔。
|
||||
|
||||
看起來像這樣:
|
||||
|
||||
```
|
||||
data: {"name": "Portal Gun", "price": 999.99}
|
||||
|
||||
data: {"name": "Plumbus", "price": 32.99}
|
||||
|
||||
```
|
||||
|
||||
SSE 常用於 AI 聊天串流、即時通知、日誌與可觀察性,以及其他由伺服器主動推送更新給用戶端的情境。
|
||||
|
||||
/// tip
|
||||
|
||||
如果你要串流二進位資料,例如影片或音訊,請參考進階指南:[串流資料](../advanced/stream-data.md)。
|
||||
|
||||
///
|
||||
|
||||
## 使用 FastAPI 串流 SSE { #stream-sse-with-fastapi }
|
||||
|
||||
要在 FastAPI 中串流 SSE,請在你的路徑操作函式(path operation function)中使用 `yield`,並設定 `response_class=EventSourceResponse`。
|
||||
|
||||
從 `fastapi.sse` 匯入 `EventSourceResponse`:
|
||||
|
||||
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
|
||||
|
||||
每個 `yield` 的項目都會以 JSON 編碼並放在 SSE 事件的 `data:` 欄位中送出。
|
||||
|
||||
如果你把回傳型別宣告為 `AsyncIterable[Item]`,FastAPI 會用它來透過 Pydantic 進行**驗證**、**文件化**與**序列化**。
|
||||
|
||||
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
|
||||
|
||||
/// tip
|
||||
|
||||
因為 Pydantic 會在 **Rust** 端進行序列化,如果你有宣告回傳型別,效能會比未宣告時高很多。
|
||||
|
||||
///
|
||||
|
||||
### 非 async 的路徑操作函式 { #non-async-path-operation-functions }
|
||||
|
||||
你也可以使用一般的 `def` 函式(沒有 `async`),並同樣使用 `yield`。
|
||||
|
||||
FastAPI 會確保正確執行,不會阻塞事件迴圈。
|
||||
|
||||
由於此函式不是 async,正確的回傳型別是 `Iterable[Item]`:
|
||||
|
||||
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
|
||||
|
||||
### 無回傳型別 { #no-return-type }
|
||||
|
||||
你也可以省略回傳型別。FastAPI 會使用 [`jsonable_encoder`](./encoder.md) 轉換資料並送出。
|
||||
|
||||
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
|
||||
|
||||
## `ServerSentEvent` { #serversentevent }
|
||||
|
||||
如果你需要設定 `event`、`id`、`retry` 或 `comment` 等 SSE 欄位,你可以改為 `yield` 出 `ServerSentEvent` 物件,而不是單純的資料。
|
||||
|
||||
從 `fastapi.sse` 匯入 `ServerSentEvent`:
|
||||
|
||||
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
|
||||
|
||||
`data` 欄位一律會以 JSON 編碼。你可以傳入任何可序列化為 JSON 的值,包括 Pydantic 模型。
|
||||
|
||||
## 原始資料 { #raw-data }
|
||||
|
||||
如果你需要在**不**進行 JSON 編碼的情況下傳送資料,請使用 `raw_data` 取代 `data`。
|
||||
|
||||
這對於傳送已格式化的文字、日誌行或特殊的 <dfn title="用於表示特殊條件或狀態的值">"哨兵"</dfn> 值(例如 `[DONE]`)很有用。
|
||||
|
||||
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
|
||||
|
||||
/// note
|
||||
|
||||
`data` 與 `raw_data` 互斥。每個 `ServerSentEvent` 只能設定其中一個。
|
||||
|
||||
///
|
||||
|
||||
## 使用 `Last-Event-ID` 繼續 { #resuming-with-last-event-id }
|
||||
|
||||
當瀏覽器在連線中斷後重新連線時,會在 `Last-Event-ID` 標頭中傳送最後接收到的 `id`。
|
||||
|
||||
你可以將它作為標頭參數讀取,並用來從用戶端中斷處繼續串流:
|
||||
|
||||
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
|
||||
|
||||
## 使用 POST 的 SSE { #sse-with-post }
|
||||
|
||||
SSE 可搭配**任何 HTTP 方法**,不僅限於 `GET`。
|
||||
|
||||
這對於像是透過 `POST` 串流 SSE 的協定(例如 [MCP](https://modelcontextprotocol.io))很有用:
|
||||
|
||||
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
|
||||
|
||||
## 技術細節 { #technical-details }
|
||||
|
||||
FastAPI 內建實作了一些 SSE 的最佳實務。
|
||||
|
||||
- 當 15 秒內沒有任何訊息時,傳送一次**「保活」`ping` 註解**,以避免某些代理伺服器關閉連線;此作法源自於[HTML 規範:Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes)中的建議。
|
||||
- 設定 `Cache-Control: no-cache` 標頭,以**防止快取**串流內容。
|
||||
- 設定特殊標頭 `X-Accel-Buffering: no`,以**避免**在像 Nginx 這類代理中被**緩衝**。
|
||||
|
||||
你不需要做任何事,開箱即用。🤓
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
# 串流 JSON Lines { #stream-json-lines }
|
||||
|
||||
當你有一連串資料想以「**串流**」方式傳送時,可以使用 **JSON Lines**。
|
||||
|
||||
/// info
|
||||
|
||||
在 FastAPI 0.134.0 新增。
|
||||
|
||||
///
|
||||
|
||||
## 什麼是串流? { #what-is-a-stream }
|
||||
|
||||
「**Streaming**」資料表示你的應用會在整個資料序列尚未完全準備好之前,就開始將資料項目傳送給用戶端。
|
||||
|
||||
也就是說,它會先送出第一個項目,用戶端接收並開始處理時,你的應用可能仍在產生下一個項目。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App
|
||||
participant Client
|
||||
|
||||
App->>App: Produce Item 1
|
||||
App->>Client: Send Item 1
|
||||
App->>App: Produce Item 2
|
||||
Client->>Client: Process Item 1
|
||||
App->>Client: Send Item 2
|
||||
App->>App: Produce Item 3
|
||||
Client->>Client: Process Item 2
|
||||
App->>Client: Send Item 3
|
||||
Client->>Client: Process Item 3
|
||||
Note over App: Keeps producing...
|
||||
Note over Client: Keeps consuming...
|
||||
```
|
||||
|
||||
它甚至可以是無限串流,你可以一直持續傳送資料。
|
||||
|
||||
## JSON Lines { #json-lines }
|
||||
|
||||
在這些情況下,常見做法是傳送「**JSON Lines**」,這是一種每一行各包含一個 JSON 物件的格式。
|
||||
|
||||
回應的 content type 會是 `application/jsonl`(而不是 `application/json`),而本體內容會像這樣:
|
||||
|
||||
```json
|
||||
{"name": "Plumbus", "description": "A multi-purpose household device."}
|
||||
{"name": "Portal Gun", "description": "A portal opening device."}
|
||||
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
|
||||
```
|
||||
|
||||
它和 JSON 陣列(相當於 Python 的 list)很像,但不同於用 `[]` 包起來並以 `,` 分隔項目,它是每一行各放一個 JSON 物件,彼此以換行字元分隔。
|
||||
|
||||
/// info
|
||||
|
||||
重點在於你的應用能夠逐行產生資料,同時用戶端在消耗前一行的資料。
|
||||
|
||||
///
|
||||
|
||||
/// note | 技術細節
|
||||
|
||||
由於每個 JSON 物件會以換行分隔,它們的內容中不能包含實際的換行字元,但可以包含跳脫後的換行(`\n`),這是 JSON 標準的一部分。
|
||||
|
||||
不過通常你不需要為此煩惱,這些都會自動處理,繼續往下看吧。🤓
|
||||
|
||||
///
|
||||
|
||||
## 使用情境 { #use-cases }
|
||||
|
||||
你可以用這種方式從 **AI LLM** 服務、**日誌**或**遙測**串流資料,或任何能以 **JSON** 項目結構化的其他型態資料。
|
||||
|
||||
/// tip
|
||||
|
||||
如果你想串流二進位資料,例如影像或音訊,請參考進階指南:[串流資料](../advanced/stream-data.md)。
|
||||
|
||||
///
|
||||
|
||||
## 使用 FastAPI 串流 JSON Lines { #stream-json-lines-with-fastapi }
|
||||
|
||||
要用 FastAPI 串流 JSON Lines,你可以在你的*路徑操作函式*中改用 `yield` 逐一產生項目,而不是用 `return`。
|
||||
|
||||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
|
||||
|
||||
如果你要回傳的每個 JSON 項目型別都是 `Item`(一個 Pydantic 模型),而且該函式是 async,你可以將回傳型別宣告為 `AsyncIterable[Item]`:
|
||||
|
||||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
|
||||
|
||||
如果你宣告了回傳型別,FastAPI 會用它來進行資料的**驗證**、在 OpenAPI 中**文件化**、**過濾**,並使用 Pydantic 進行**序列化**。
|
||||
|
||||
/// tip
|
||||
|
||||
由於 Pydantic 會在 **Rust** 端進行序列化,宣告回傳型別可獲得比未宣告時高得多的**效能**。
|
||||
|
||||
///
|
||||
|
||||
### 非 async 的*路徑操作函式* { #non-async-path-operation-functions }
|
||||
|
||||
你也可以用一般的 `def` 函式(沒有 `async`),同樣用 `yield`。
|
||||
|
||||
FastAPI 會確保正確執行,不會阻塞事件迴圈。
|
||||
|
||||
因為這種情況下函式不是 async,正確的回傳型別會是 `Iterable[Item]`:
|
||||
|
||||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
|
||||
|
||||
### 不宣告回傳型別 { #no-return-type }
|
||||
|
||||
你也可以省略回傳型別。此時 FastAPI 會使用 [`jsonable_encoder`](./encoder.md) 將資料轉換為可序列化為 JSON 的形式,然後以 JSON Lines 傳送。
|
||||
|
||||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
|
||||
|
||||
## 伺服器推播事件(SSE) { #server-sent-events-sse }
|
||||
|
||||
FastAPI 也原生支援 Server-Sent Events(SSE),它們與此相當類似,但多了幾個細節。你可以在下一章學到更多:[伺服器推播事件(SSE)](server-sent-events.md)。🤓
|
||||
Loading…
Reference in New Issue