Browse Source
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>pull/15173/head
committed by
GitHub
6 changed files with 522 additions and 0 deletions
@ -0,0 +1,63 @@ |
|||
# Base64 にしたバイトを含む JSON { #json-with-bytes-as-base64 } |
|||
|
|||
アプリで JSON データの受信・送信が必要だが、その中にバイナリデータを含める必要がある場合は、base64 にエンコードできます。 |
|||
|
|||
## Base64 とファイル { #base64-vs-files } |
|||
|
|||
バイナリデータのアップロードにはまず、JSON にエンコードする代わりに [Request Files](../tutorial/request-files.md) を、バイナリデータの送信には [カスタムレスポンス - FileResponse](./custom-response.md#fileresponse--fileresponse-) を使えるか検討してください。 |
|||
|
|||
JSON は UTF-8 でエンコードされた文字列のみを含められるため、生のバイト列は含められません。 |
|||
|
|||
Base64 はバイナリデータを文字列にエンコードできますが、そのために元のバイナリより多くの文字を使用する必要があり、通常は通常のファイルより非効率です。 |
|||
|
|||
JSON にバイナリデータをどうしても含める必要があり、ファイルを使えない場合にのみ base64 を使用してください。 |
|||
|
|||
## Pydantic `bytes` { #pydantic-bytes } |
|||
|
|||
Pydantic モデルで `bytes` 型のフィールドを宣言し、モデル設定で `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` フィールドを使用することもでき、Pydantic は JSON レスポンスを生成するときにバイト列を 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 を使うように設定しておけば、JSON データの受信時は `val_json_bytes` で入力を「検証」し、送信時は `ser_json_bytes` で出力を「シリアライズ」する、といった具合に、入力と出力の両方を扱えます。 |
|||
|
|||
{* ../../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 サービスの出力をそのまま、純粋な文字列としてストリームしたい場合に使えます。 |
|||
|
|||
メモリに一度に全て読み込むことなく、読み込みながらチャンクごとに送ることで、巨大なバイナリファイルをストリームすることにも使えます。 |
|||
|
|||
同様に、動画や音声をストリームすることもできます。処理しながら生成し、そのまま送信することも可能です。 |
|||
|
|||
## `yield` を使った `StreamingResponse` { #a-streamingresponse-with-yield } |
|||
|
|||
path operation 関数で `response_class=StreamingResponse` を宣言すると、`yield` を使ってデータをチャンクごとに順次送信できます。 |
|||
|
|||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *} |
|||
|
|||
FastAPI は各データチャンクをそのまま `StreamingResponse` に渡し、JSON などに変換しようとはしません。 |
|||
|
|||
### 非 async な path operation 関数 { #non-async-path-operation-functions } |
|||
|
|||
`async` なしの通常の `def` 関数でも同様に `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` ヘッダを設定できます。 |
|||
|
|||
例えば、`media_type` 属性で `Content-Type` を `image/png` に設定する `PNGStreamingResponse` を作成できます: |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *} |
|||
|
|||
その後、path operation 関数で `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 | 技術詳細 |
|||
|
|||
他の2つの変数 `image_base64` と `binary_image` は、画像を Base64 でエンコードし、それを `bytes` に変換してから `io.BytesIO` に渡したものです。 |
|||
|
|||
この例では1つのファイル内に完結させ、コピーしてそのまま実行できるようにするためだけのものです。 🥚 |
|||
|
|||
/// |
|||
|
|||
`with` ブロックを使うことで、ジェネレータ関数(`yield` を持つ関数)が終了した後、つまりレスポンス送信が完了した後に、そのファイルライクオブジェクトが確実にクローズされます。 |
|||
|
|||
この例では `io.BytesIO` によるメモリ内の疑似ファイルなので重要度は高くありませんが、実ファイルの場合は処理後に確実にクローズすることが重要です。 |
|||
|
|||
### ファイルと非同期 { #files-and-async } |
|||
|
|||
多くの場合、ファイルライクオブジェクトはデフォルトでは async/await と互換性がありません。 |
|||
|
|||
例えば、`await file.read()` や `async for chunk in file` のような操作は提供されていません。 |
|||
|
|||
また、多くの場合、ディスクやネットワークから読み出すため、読み取りはブロッキング(イベントループをブロックし得る)処理になります。 |
|||
|
|||
/// info | 情報 |
|||
|
|||
上記の例は例外で、`io.BytesIO` は既にメモリ上にあるため、読み取りが何かをブロックすることはありません。 |
|||
|
|||
しかし多くの場合、ファイルやファイルライクオブジェクトの読み取りはブロッキングになります。 |
|||
|
|||
/// |
|||
|
|||
イベントループのブロッキングを避けるには、path operation 関数を `async def` ではなく通常の `def` で宣言してください。そうすると FastAPI はその関数をスレッドプールワーカー上で実行し、メインループのブロッキングを避けます。 |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *} |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
async 関数内からブロッキングなコードを呼び出す必要がある場合、あるいはブロッキングな関数内から async 関数を呼び出す必要がある場合は、FastAPI の兄弟ライブラリである [Asyncer](https://asyncer.tiangolo.com) を利用できます。 |
|||
|
|||
/// |
|||
|
|||
### `yield from` { #yield-from } |
|||
|
|||
ファイルライクオブジェクトのようなものをイテレートして各要素に対して `yield` している場合、`for` ループを省略して、`yield from` で各要素をそのまま送ることもできます。 |
|||
|
|||
これは 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 のリクエストを JSON として解析するには、有効な `Content-Type` ヘッダー(例: `application/json`)を必ず含める必要があります。 |
|||
|
|||
## CSRF のリスク { #csrf-risk } |
|||
|
|||
この既定の挙動は、ある特定の状況における Cross-Site Request Forgery(CSRF)攻撃の一種に対する保護を提供します。 |
|||
|
|||
これらの攻撃は、次の条件を満たすときにブラウザが CORS のプリフライトチェックを行わずにスクリプトからリクエストを送信できる事実を悪用します。 |
|||
|
|||
- `Content-Type` ヘッダーがない(例: `Blob` をボディにして `fetch()` を使う) |
|||
- かつ、いかなる認証情報も送信しない |
|||
|
|||
この種の攻撃は主に次のような場合に関係します。 |
|||
|
|||
- アプリケーションがローカル(例: `localhost`)または社内ネットワークで動作している |
|||
- かつ、アプリに認証がなく、同一ネットワークからのリクエストは信頼できると想定している |
|||
|
|||
## 攻撃例 { #example-attack } |
|||
|
|||
ローカルで AI エージェントを実行できる仕組みを構築したとします。 |
|||
|
|||
それは次の API を提供します。 |
|||
|
|||
``` |
|||
http://localhost:8000/v1/agents/multivac |
|||
``` |
|||
|
|||
フロントエンドもあります。 |
|||
|
|||
``` |
|||
http://localhost:8000 |
|||
``` |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
両方とも同じホストであることに注意してください。 |
|||
|
|||
/// |
|||
|
|||
そのフロントエンドを使って、AI エージェントに自分の代わりに作業をさせられます。 |
|||
|
|||
それが「公開インターネット」ではなくローカルで動作しているため、ローカルネットワークへのアクセスを信頼し、認証を一切設定しないことにしたとします。 |
|||
|
|||
すると、ユーザーの一人がそれをインストールしてローカルで実行できます。 |
|||
|
|||
その後、例えば次のような悪意のあるサイトを開く可能性があります。 |
|||
|
|||
``` |
|||
https://evilhackers.example.com |
|||
``` |
|||
|
|||
そしてその悪意のあるサイトが、`Blob` をボディにした `fetch()` を使ってローカルの 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 Extension](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 Extension** は [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 エクスプローラー** - アプリケーション内のすべての <dfn title="ルート、エンドポイント">*path operations*</dfn> をサイドバーのツリービューで表示します。クリックして任意のルートまたはルーター定義へジャンプできます。 |
|||
- **ルート検索** - <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd>(macOS: <kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd>)で、パス・メソッド・名前で検索できます。 |
|||
- **CodeLens ナビゲーション** - テストクライアント呼び出し(例: `client.get('/items')`)の上に表示されるクリック可能なリンクから、対応する *path operation* にジャンプし、テストと実装の行き来をすばやく行えます。 |
|||
- **FastAPI Cloud へデプロイ** - [FastAPI Cloud](https://fastapicloud.com/) にワンクリックでアプリをデプロイできます。 |
|||
- **アプリケーションログのストリーミング** - 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" のウォークスルーを選んでください。 |
|||
@ -0,0 +1,120 @@ |
|||
# Server-Sent Events (SSE) { #server-sent-events-sse } |
|||
|
|||
**Server-Sent Events** (SSE) を使うと、クライアントへデータをストリーミングできます。 |
|||
|
|||
これは[JSON Lines のストリーミング](stream-json-lines.md)に似ていますが、`text/event-stream` フォーマットを使用します。これはブラウザがネイティブに [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) でサポートしています。 |
|||
|
|||
/// 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 関数*で `yield` を使い、`response_class=EventSourceResponse` を設定します。 |
|||
|
|||
`EventSourceResponse` は `fastapi.sse` からインポートします: |
|||
|
|||
{* ../../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 の *path operation 関数* { #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 フィールドを設定する必要がある場合は、生データの代わりに `ServerSentEvent` オブジェクトを `yield` できます。 |
|||
|
|||
`ServerSentEvent` は `fastapi.sse` からインポートします: |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *} |
|||
|
|||
`data` フィールドは常に JSON にエンコードされます。Pydantic モデルを含む、JSON にシリアライズ可能な任意の値を渡せます。 |
|||
|
|||
## 生データ { #raw-data } |
|||
|
|||
JSON エンコードせずにデータを送る必要がある場合は、`data` の代わりに `raw_data` を使用します。 |
|||
|
|||
これは、整形済みテキスト、ログ行、または `[DONE]` のような特別な <dfn title="特別な条件や状態を示すために用いられる値">"センチネル"</dfn> 値を送るのに有用です。 |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *} |
|||
|
|||
/// note | 備考 |
|||
|
|||
`data` と `raw_data` は相互排他的です。各 `ServerSentEvent` ではどちらか一方しか設定できません。 |
|||
|
|||
/// |
|||
|
|||
## `Last-Event-ID` での再開 { #resuming-with-last-event-id } |
|||
|
|||
接続が途切れた後にブラウザが再接続すると、最後に受信した `id` を `Last-Event-ID` ヘッダーで送信します。 |
|||
|
|||
これをヘッダーパラメータとして受け取り、クライアントが離脱した位置からストリームを再開できます: |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *} |
|||
|
|||
## POST での SSE { #sse-with-post } |
|||
|
|||
SSE は `GET` だけでなく、**任意の HTTP メソッド**で動作します。 |
|||
|
|||
これは、`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` ヘッダーを設定します。 |
|||
- Nginx など一部のプロキシでの**バッファリングを防ぐ**ため、特別なヘッダー `X-Accel-Buffering: no` を設定します。 |
|||
|
|||
追加の設定は不要で、そのまま動作します。🤓 |
|||
@ -0,0 +1,111 @@ |
|||
# JSON Lines をストリームする { #stream-json-lines } |
|||
|
|||
データのシーケンスを**「ストリーム」**で送りたい場合、**JSON Lines** を使って実現できます。 |
|||
|
|||
/// info | 情報 |
|||
|
|||
FastAPI 0.134.0 で追加されました。 |
|||
|
|||
/// |
|||
|
|||
## ストリームとは { #what-is-a-stream } |
|||
|
|||
データを**ストリーミング**するとは、アイテムの全シーケンスが用意できるのを待たずに、アプリがデータアイテムの送信をクライアントに対して開始することを意味します。 |
|||
|
|||
つまり、最初のアイテムを送信し、クライアントはそれを受け取って処理を始めます。その間に、次のアイテムをまだ生成しているかもしれません。 |
|||
|
|||
```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 } |
|||
|
|||
このような場合、1 行に 1 つの JSON オブジェクトを送る「**JSON Lines**」形式を使うのが一般的です。 |
|||
|
|||
レスポンスの 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 に相当)にとてもよく似ていますが、`[]` で囲まず、アイテム間の `,` もありません。その代わりに、**1 行に 1 つの 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 をストリームするには、*path operation 関数*で `return` を使う代わりに、`yield` を使って各アイテムを順に生成します。 |
|||
|
|||
{* ../../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 の *path operation 関数* { #non-async-path-operation-functions } |
|||
|
|||
`async` を使わない通常の `def` 関数でも同様に `yield` を使えます。 |
|||
|
|||
FastAPI が適切に実行されるように処理するため、イベントループをブロックしません。 |
|||
|
|||
この場合は関数が async ではないので、適切な戻り値の型は `Iterable[Item]` です: |
|||
|
|||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *} |
|||
|
|||
### 戻り値の型なし { #no-return-type } |
|||
|
|||
戻り値の型を省略することもできます。FastAPI はその場合、データを JSON にシリアライズ可能な形に変換するために [`jsonable_encoder`](./encoder.md) を使い、JSON Lines として送信します。 |
|||
|
|||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *} |
|||
|
|||
## Server-Sent Events (SSE) { #server-sent-events-sse } |
|||
|
|||
FastAPI は Server-Sent Events (SSE) にもファーストクラスで対応しています。とても似ていますが、いくつか追加の詳細があります。次の章で学べます: [Server-Sent Events (SSE)](server-sent-events.md)。🤓 |
|||
Loading…
Reference in new issue