Sync fastapi docs from b5ca1324 on 2025-12-07
Issue Manager / issue-manager (push) Has been cancelled
Build Docs / changes (push) Has been cancelled
Build Docs / langs (push) Has been cancelled
Build Docs / build-docs (push) Has been cancelled
Build Docs / docs-all-green (push) Has been cancelled
Conflict detector / main (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi) (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi-slim) (push) Has been cancelled
Test Redistribute / test-redistribute-alls-green (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / test (pydantic-v1, 3.10) (push) Has been cancelled
Test / test (pydantic-v1, 3.11) (push) Has been cancelled
Test / test (pydantic-v1, 3.13) (push) Has been cancelled
Test / test (pydantic-v1, 3.8) (push) Has been cancelled
Test / test (pydantic-v1, 3.9) (push) Has been cancelled
Test / test (pydantic-v2, 3.10) (push) Has been cancelled
Test / test (pydantic-v2, 3.11) (push) Has been cancelled
Test / test (pydantic-v2, 3.12) (push) Has been cancelled
Test / test (pydantic-v2, 3.13) (push) Has been cancelled
Test / test (pydantic-v2, 3.14) (push) Has been cancelled
Test / test (pydantic-v2, 3.8) (push) Has been cancelled
Test / test (pydantic-v2, 3.9) (push) Has been cancelled
Test / coverage-combine (push) Has been cancelled
Test / check (push) Has been cancelled
Label Approved / label-approved (push) Has been cancelled
FastAPI People Contributors / job (push) Has been cancelled
FastAPI People Sponsors / job (push) Has been cancelled
Update Topic Repos / topic-repos (push) Has been cancelled
FastAPI People / job (push) Has been cancelled
Test / test (pydantic-v1, 3.12) (push) Has been cancelled
Issue Manager / issue-manager (push) Has been cancelled
Build Docs / changes (push) Has been cancelled
Build Docs / langs (push) Has been cancelled
Build Docs / build-docs (push) Has been cancelled
Build Docs / docs-all-green (push) Has been cancelled
Conflict detector / main (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi) (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi-slim) (push) Has been cancelled
Test Redistribute / test-redistribute-alls-green (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / test (pydantic-v1, 3.10) (push) Has been cancelled
Test / test (pydantic-v1, 3.11) (push) Has been cancelled
Test / test (pydantic-v1, 3.13) (push) Has been cancelled
Test / test (pydantic-v1, 3.8) (push) Has been cancelled
Test / test (pydantic-v1, 3.9) (push) Has been cancelled
Test / test (pydantic-v2, 3.10) (push) Has been cancelled
Test / test (pydantic-v2, 3.11) (push) Has been cancelled
Test / test (pydantic-v2, 3.12) (push) Has been cancelled
Test / test (pydantic-v2, 3.13) (push) Has been cancelled
Test / test (pydantic-v2, 3.14) (push) Has been cancelled
Test / test (pydantic-v2, 3.8) (push) Has been cancelled
Test / test (pydantic-v2, 3.9) (push) Has been cancelled
Test / coverage-combine (push) Has been cancelled
Test / check (push) Has been cancelled
Label Approved / label-approved (push) Has been cancelled
FastAPI People Contributors / job (push) Has been cancelled
FastAPI People Sponsors / job (push) Has been cancelled
Update Topic Repos / topic-repos (push) Has been cancelled
FastAPI People / job (push) Has been cancelled
Test / test (pydantic-v1, 3.12) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# 追加のステータスコード
|
||||
|
||||
デフォルトでは、 **FastAPI** は `JSONResponse` を使ってレスポンスを返します。その `JSONResponse` の中には、 *path operation* が返した内容が入ります。
|
||||
|
||||
それは、デフォルトのステータスコードか、 *path operation* でセットしたものを利用します。
|
||||
|
||||
## 追加のステータスコード
|
||||
|
||||
メインのステータスコードとは別に、他のステータスコードを返したい場合は、`Response` (`JSONResponse` など) に追加のステータスコードを設定して直接返します。
|
||||
|
||||
例えば、itemを更新し、成功した場合は200 "OK"のHTTPステータスコードを返す *path operation* を作りたいとします。
|
||||
|
||||
しかし、新しいitemも許可したいです。itemが存在しない場合は、それらを作成して201 "Created"を返します。
|
||||
|
||||
これを達成するには、 `JSONResponse` をインポートし、 `status_code` を設定して直接内容を返します。
|
||||
|
||||
{* ../../docs_src/additional_status_codes/tutorial001.py hl[4,25] *}
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
上記の例のように `Response` を明示的に返す場合、それは直接返されます。
|
||||
|
||||
モデルなどはシリアライズされません。
|
||||
|
||||
必要なデータが含まれていることや、値が有効なJSONであること (`JSONResponse` を使う場合) を確認してください。
|
||||
|
||||
///
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`from starlette.responses import JSONResponse` を利用することもできます。
|
||||
|
||||
**FastAPI** は `fastapi.responses` と同じ `starlette.responses` を、開発者の利便性のために提供しています。しかし有効なレスポンスはほとんどStarletteから来ています。 `status` についても同じです。
|
||||
|
||||
///
|
||||
|
||||
## OpenAPIとAPIドキュメント
|
||||
|
||||
ステータスコードとレスポンスを直接返す場合、それらはOpenAPIスキーマ (APIドキュメント) には含まれません。なぜなら、FastAPIは何が返されるのか事前に知ることができないからです。
|
||||
|
||||
しかし、 [Additional Responses](additional-responses.md){.internal-link target=_blank} を使ってコードの中にドキュメントを書くことができます。
|
||||
@@ -0,0 +1,232 @@
|
||||
# カスタムレスポンス - HTML、ストリーム、ファイル、その他のレスポンス
|
||||
|
||||
デフォルトでは、**FastAPI** は `JSONResponse` を使ってレスポンスを返します。
|
||||
|
||||
[レスポンスを直接返す](response-directly.md){.internal-link target=_blank}で見たように、 `Response` を直接返すことでこの挙動をオーバーライドできます。
|
||||
|
||||
しかし、`Response` を直接返すと、データは自動的に変換されず、ドキュメントも自動生成されません (例えば、生成されるOpenAPIの一部としてHTTPヘッダー `Content-Type` に特定の「メディアタイプ」を含めるなど) 。
|
||||
|
||||
しかし、*path operationデコレータ* に、使いたい `Response` を宣言することもできます。
|
||||
|
||||
*path operation関数* から返されるコンテンツは、その `Response` に含まれます。
|
||||
|
||||
そしてもし、`Response` が、`JSONResponse` や `UJSONResponse` の場合のようにJSONメディアタイプ (`application/json`) ならば、データは *path operationデコレータ* に宣言したPydantic `response_model` により自動的に変換 (もしくはフィルタ) されます。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
メディアタイプを指定せずにレスポンスクラスを利用すると、FastAPIは何もコンテンツがないことを期待します。そのため、生成されるOpenAPIドキュメントにレスポンスフォーマットが記載されません。
|
||||
|
||||
///
|
||||
|
||||
## `ORJSONResponse` を使う
|
||||
|
||||
例えば、パフォーマンスを出したい場合は、<a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>をインストールし、`ORJSONResponse`をレスポンスとしてセットすることができます。
|
||||
|
||||
使いたい `Response` クラス (サブクラス) をインポートし、 *path operationデコレータ* に宣言します。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
パラメータ `response_class` は、レスポンスの「メディアタイプ」を定義するために利用することもできます。
|
||||
|
||||
この場合、HTTPヘッダー `Content-Type` には `application/json` がセットされます。
|
||||
|
||||
そして、OpenAPIにはそのようにドキュメントされます。
|
||||
|
||||
///
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`ORJSONResponse` は、現在はFastAPIのみで利用可能で、Starletteでは利用できません。
|
||||
|
||||
///
|
||||
|
||||
## HTMLレスポンス
|
||||
|
||||
**FastAPI** からHTMLを直接返す場合は、`HTMLResponse` を使います。
|
||||
|
||||
* `HTMLResponse` をインポートする。
|
||||
* *path operation* のパラメータ `content_type` に `HTMLResponse` を渡す。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
パラメータ `response_class` は、レスポンスの「メディアタイプ」を定義するために利用されます。
|
||||
|
||||
この場合、HTTPヘッダー `Content-Type` には `text/html` がセットされます。
|
||||
|
||||
そして、OpenAPIにはそのようにドキュメント化されます。
|
||||
|
||||
///
|
||||
|
||||
### `Response` を返す
|
||||
|
||||
[レスポンスを直接返す](response-directly.md){.internal-link target=_blank}で見たように、レスポンスを直接返すことで、*path operation* の中でレスポンスをオーバーライドできます。
|
||||
|
||||
上記と同じ例において、 `HTMLResponse` を返すと、このようになります:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
*path operation関数* から直接返される `Response` は、OpenAPIにドキュメントされず (例えば、 `Content-Type` がドキュメントされない) 、自動的な対話的ドキュメントからも閲覧できません。
|
||||
|
||||
///
|
||||
|
||||
/// info | 情報
|
||||
|
||||
もちろん、実際の `Content-Type` ヘッダーやステータスコードなどは、返された `Response` オブジェクトに由来しています。
|
||||
|
||||
///
|
||||
|
||||
### OpenAPIドキュメントと `Response` のオーバーライド
|
||||
|
||||
関数の中でレスポンスをオーバーライドしつつも、OpenAPI に「メディアタイプ」をドキュメント化したいなら、 `response_class` パラメータを使い、 `Response` オブジェクトを返します。
|
||||
|
||||
`response_class` はOpenAPIの *path operation* ドキュメントにのみ使用されますが、 `Response` はそのまま使用されます。
|
||||
|
||||
#### `HTMLResponse` を直接返す
|
||||
|
||||
例えば、このようになります:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
|
||||
|
||||
この例では、関数 `generate_html_response()` は、`str` のHTMLを返すのではなく `Response` を生成して返しています。
|
||||
|
||||
`generate_html_response()` を呼び出した結果を返すことにより、**FastAPI** の振る舞いを上書きする `Response` が既に返されています。
|
||||
|
||||
しかし、一方では `response_class` に `HTMLResponse` を渡しているため、 **FastAPI** はOpenAPIや対話的ドキュメントでHTMLとして `text/html` でドキュメント化する方法を知っています。
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## 利用可能なレスポンス
|
||||
|
||||
以下が利用可能なレスポンスの一部です。
|
||||
|
||||
`Response` を使って他の何かを返せますし、カスタムのサブクラスも作れることを覚えておいてください。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`from starlette.responses import HTMLResponse` も利用できます。
|
||||
|
||||
**FastAPI** は開発者の利便性のために `fastapi.responses` として `starlette.responses` と同じものを提供しています。しかし、利用可能なレスポンスのほとんどはStarletteから直接提供されます。
|
||||
|
||||
///
|
||||
|
||||
### `Response`
|
||||
|
||||
メインの `Response` クラスで、他の全てのレスポンスはこれを継承しています。
|
||||
|
||||
直接返すことができます。
|
||||
|
||||
以下のパラメータを受け付けます。
|
||||
|
||||
* `content` - `str` か `bytes`。
|
||||
* `status_code` - `int` のHTTPステータスコード。
|
||||
* `headers` - 文字列の `dict` 。
|
||||
* `media_type` - メディアタイプを示す `str` 。例えば `"text/html"` 。
|
||||
|
||||
FastAPI (実際にはStarlette) は自動的にContent-Lengthヘッダーを含みます。また、media_typeに基づいたContent-Typeヘッダーを含み、テキストタイプのためにcharsetを追加します。
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse`
|
||||
|
||||
上で読んだように、テキストやバイトを受け取り、HTMLレスポンスを返します。
|
||||
|
||||
### `PlainTextResponse`
|
||||
|
||||
テキストやバイトを受け取り、プレーンテキストのレスポンスを返します。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse`
|
||||
|
||||
データを受け取り、 `application/json` としてエンコードされたレスポンスを返します。
|
||||
|
||||
上で読んだように、**FastAPI** のデフォルトのレスポンスとして利用されます。
|
||||
|
||||
### `ORJSONResponse`
|
||||
|
||||
上で読んだように、<a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>を使った、高速な代替のJSONレスポンスです。
|
||||
|
||||
### `UJSONResponse`
|
||||
|
||||
<a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>を使った、代替のJSONレスポンスです。
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
`ujson` は、いくつかのエッジケースの取り扱いについて、Pythonにビルトインされた実装よりも作りこまれていません。
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`ORJSONResponse` のほうが高速な代替かもしれません。
|
||||
|
||||
///
|
||||
|
||||
### `RedirectResponse`
|
||||
|
||||
HTTPリダイレクトを返します。デフォルトでは307ステータスコード (Temporary Redirect) となります。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
|
||||
|
||||
### `StreamingResponse`
|
||||
|
||||
非同期なジェネレータか通常のジェネレータ・イテレータを受け取り、レスポンスボディをストリームします。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
|
||||
|
||||
#### `StreamingResponse` をファイルライクなオブジェクトとともに使う
|
||||
|
||||
ファイルライクなオブジェクト (例えば、 `open()` で返されたオブジェクト) がある場合、 `StreamingResponse` に含めて返すことができます。
|
||||
|
||||
これにはクラウドストレージとの連携や映像処理など、多くのライブラリが含まれています。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
ここでは `async` や `await` をサポートしていない標準の `open()` を使っているので、通常の `def` でpath operationを宣言していることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
### `FileResponse`
|
||||
|
||||
レスポンスとしてファイルを非同期的にストリームします。
|
||||
|
||||
他のレスポンスタイプとは異なる引数のセットを受け取りインスタンス化します。
|
||||
|
||||
* `path` - ストリームするファイルのファイルパス。
|
||||
* `headers` - 含めたい任意のカスタムヘッダーの辞書。
|
||||
* `media_type` - メディアタイプを示す文字列。セットされなかった場合は、ファイル名やパスからメディアタイプが推察されます。
|
||||
* `filename` - セットされた場合、レスポンスの `Content-Disposition` に含まれます。
|
||||
|
||||
ファイルレスポンスには、適切な `Content-Length` 、 `Last-Modified` 、 `ETag` ヘッダーが含まれます。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
|
||||
|
||||
## デフォルトレスポンスクラス
|
||||
|
||||
**FastAPI** クラスのインスタンスか `APIRouter` を生成するときに、デフォルトのレスポンスクラスを指定できます。
|
||||
|
||||
定義するためのパラメータは、 `default_response_class` です。
|
||||
|
||||
以下の例では、 **FastAPI** は、全ての *path operation* で `JSONResponse` の代わりに `ORJSONResponse` をデフォルトとして利用します。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
前に見たように、 *path operation* の中で `response_class` をオーバーライドできます。
|
||||
|
||||
///
|
||||
|
||||
## その他のドキュメント
|
||||
|
||||
また、OpenAPIでは `responses` を使ってメディアタイプやその他の詳細を宣言することもできます: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}
|
||||
@@ -0,0 +1,27 @@
|
||||
# 高度なユーザーガイド
|
||||
|
||||
## さらなる機能
|
||||
|
||||
[チュートリアル - ユーザーガイド](../tutorial/index.md){.internal-link target=_blank}により、**FastAPI**の主要な機能は十分に理解できたことでしょう。
|
||||
|
||||
以降のセクションでは、チュートリアルでは説明しきれなかったオプションや設定、および機能について説明します。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
以降のセクションは、 **必ずしも"応用編"ではありません**。
|
||||
|
||||
ユースケースによっては、その中から解決策を見つけられるかもしれません。
|
||||
|
||||
///
|
||||
|
||||
## 先にチュートリアルを読む
|
||||
|
||||
[チュートリアル - ユーザーガイド](../tutorial/index.md){.internal-link target=_blank}の知識があれば、**FastAPI**の主要な機能を利用することができます。
|
||||
|
||||
以降のセクションは、すでにチュートリアルを読んで、その主要なアイデアを理解できていることを前提としています。
|
||||
|
||||
## テスト駆動開発のコース
|
||||
|
||||
このセクションの内容を補完するために脱初心者用コースを受けたい場合は、**TestDriven.io**による、<a href="https://testdriven.io/courses/tdd-fastapi/" class="external-link" target="_blank">Test-Driven Development with FastAPI and Docker</a>を確認するのがよいかもしれません。
|
||||
|
||||
現在、このコースで得られた利益の10%が**FastAPI**の開発のために寄付されています。🎉 😄
|
||||
@@ -0,0 +1,53 @@
|
||||
# Path Operationの高度な設定
|
||||
|
||||
## OpenAPI operationId
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
あなたがOpenAPIの「エキスパート」でなければ、これは必要ないかもしれません。
|
||||
|
||||
///
|
||||
|
||||
*path operation* で `operation_id` パラメータを利用することで、OpenAPIの `operationId` を設定できます。
|
||||
|
||||
`operation_id` は各オペレーションで一意にする必要があります。
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
|
||||
|
||||
### *path operation関数* の名前をoperationIdとして使用する
|
||||
|
||||
APIの関数名を `operationId` として利用したい場合、すべてのAPIの関数をイテレーションし、各 *path operation* の `operationId` を `APIRoute.name` で上書きすれば可能です。
|
||||
|
||||
そうする場合は、すべての *path operation* を追加した後に行う必要があります。
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2,12:21,24] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`app.openapi()` を手動でコールする場合、その前に`operationId`を更新する必要があります。
|
||||
|
||||
///
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
この方法をとる場合、各 *path operation関数* が一意な名前である必要があります。
|
||||
|
||||
それらが異なるモジュール (Pythonファイル) にあるとしてもです。
|
||||
|
||||
///
|
||||
|
||||
## OpenAPIから除外する
|
||||
|
||||
生成されるOpenAPIスキーマ (つまり、自動ドキュメント生成の仕組み) から *path operation* を除外するには、 `include_in_schema` パラメータを `False` にします。
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
|
||||
|
||||
## docstringによる説明の高度な設定
|
||||
|
||||
*path operation関数* のdocstringからOpenAPIに使用する行を制限することができます。
|
||||
|
||||
`\f` (「書式送り (Form Feed)」のエスケープ文字) を付与することで、**FastAPI** はOpenAPIに使用される出力をその箇所までに制限します。
|
||||
|
||||
ドキュメントには表示されませんが、他のツール (例えばSphinx) では残りの部分を利用できるでしょう。
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
|
||||
@@ -0,0 +1,65 @@
|
||||
# レスポンスを直接返す
|
||||
|
||||
**FastAPI** の *path operation* では、通常は任意のデータを返すことができます: 例えば、 `dict`、`list`、Pydanticモデル、データベースモデルなどです。
|
||||
|
||||
デフォルトでは、**FastAPI** は [JSON互換エンコーダ](../tutorial/encoder.md){.internal-link target=_blank} で説明されている `jsonable_encoder` により、返す値を自動的にJSONに変換します。
|
||||
|
||||
このとき背後では、JSON互換なデータ (例えば`dict`) を、クライアントへ送信されるレスポンスとして利用される `JSONResponse` の中に含めます。
|
||||
|
||||
しかし、*path operation* から `JSONResponse` を直接返すこともできます。
|
||||
|
||||
これは例えば、カスタムヘッダーやcookieを返すときに便利です。
|
||||
|
||||
## `Response` を返す
|
||||
|
||||
実際は、`Response` やそのサブクラスを返すことができます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`JSONResponse` それ自体は、 `Response` のサブクラスです。
|
||||
|
||||
///
|
||||
|
||||
`Response` を返した場合は、**FastAPI** は直接それを返します。
|
||||
|
||||
それは、Pydanticモデルのデータ変換や、コンテンツを任意の型に変換したりなどはしません。
|
||||
|
||||
これは多くの柔軟性を提供します。任意のデータ型を返したり、任意のデータ宣言やバリデーションをオーバーライドできます。
|
||||
|
||||
## `jsonable_encoder` を `Response` の中で使う
|
||||
|
||||
**FastAPI** はあなたが返す `Response` に対して何も変更を加えないので、コンテンツが準備できていることを保証しなければなりません。
|
||||
|
||||
例えば、Pydanticモデルを `JSONResponse` に含めるには、すべてのデータ型 (`datetime` や `UUID` など) をJSON互換の型に変換された `dict` に変換しなければなりません。
|
||||
|
||||
このようなケースでは、レスポンスにデータを含める前に `jsonable_encoder` を使ってデータを変換できます。
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
また、`from starlette.responses import JSONResponse` も利用できます。
|
||||
|
||||
**FastAPI** は開発者の利便性のために `fastapi.responses` という `starlette.responses` と同じものを提供しています。しかし、利用可能なレスポンスのほとんどはStarletteから直接提供されます。
|
||||
|
||||
///
|
||||
|
||||
## カスタム `Response` を返す
|
||||
|
||||
上記の例では必要な部分を全て示していますが、あまり便利ではありません。`item` を直接返すことができるし、**FastAPI** はそれを `dict` に変換して `JSONResponse` に含めてくれるなど。すべて、デフォルトの動作です。
|
||||
|
||||
では、これを使ってカスタムレスポンスをどう返すか見てみましょう。
|
||||
|
||||
<a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a>レスポンスを返したいとしましょう。
|
||||
|
||||
XMLを文字列にし、`Response` に含め、それを返します。
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
|
||||
|
||||
## 備考
|
||||
|
||||
`Response` を直接返す場合、バリデーションや、変換 (シリアライズ) や、自動ドキュメントは行われません。
|
||||
|
||||
しかし、[Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}に記載されたようにドキュメントを書くこともできます。
|
||||
|
||||
後のセクションで、カスタム `Response` を使用・宣言しながら、自動的なデータ変換やドキュメンテーションを行う方法を説明します。
|
||||
@@ -0,0 +1,188 @@
|
||||
# WebSocket
|
||||
|
||||
**FastAPI**で<a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSocket</a>が使用できます。
|
||||
|
||||
## `WebSockets`のインストール
|
||||
|
||||
まず `WebSockets`のインストールが必要です。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install websockets
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## WebSocket クライアント
|
||||
|
||||
### 本番環境
|
||||
|
||||
本番環境では、React、Vue.js、Angularなどの最新のフレームワークで作成されたフロントエンドを使用しているでしょう。
|
||||
|
||||
そして、バックエンドとWebSocketを使用して通信するために、おそらくフロントエンドのユーティリティを使用することになるでしょう。
|
||||
|
||||
または、ネイティブコードでWebSocketバックエンドと直接通信するネイティブモバイルアプリケーションがあるかもしれません。
|
||||
|
||||
他にも、WebSocketのエンドポイントと通信する方法があるかもしれません。
|
||||
|
||||
---
|
||||
|
||||
ただし、この例では非常にシンプルなHTML文書といくつかのJavaScriptを、すべてソースコードの中に入れて使用することにします。
|
||||
|
||||
もちろん、これは最適な方法ではありませんし、本番環境で使うことはないでしょう。
|
||||
|
||||
本番環境では、上記の方法のいずれかの選択肢を採用することになるでしょう。
|
||||
|
||||
しかし、これはWebSocketのサーバーサイドに焦点を当て、実用的な例を示す最も簡単な方法です。
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
|
||||
|
||||
## `websocket` を作成する
|
||||
|
||||
**FastAPI** アプリケーションで、`websocket` を作成します。
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`from starlette.websockets import WebSocket` を使用しても構いません.
|
||||
|
||||
**FastAPI** は開発者の利便性のために、同じ `WebSocket` を提供します。しかし、こちらはStarletteから直接提供されるものです。
|
||||
|
||||
///
|
||||
|
||||
## メッセージの送受信
|
||||
|
||||
WebSocketルートでは、 `await` を使ってメッセージの送受信ができます。
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
|
||||
|
||||
バイナリやテキストデータ、JSONデータを送受信できます。
|
||||
|
||||
## 試してみる
|
||||
|
||||
ファイル名が `main.py` である場合、以下の方法でアプリケーションを実行します。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
ブラウザで <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a> を開きます。
|
||||
|
||||
次のようなシンプルなページが表示されます。
|
||||
|
||||
<img src="/img/tutorial/websockets/image01.png">
|
||||
|
||||
入力ボックスにメッセージを入力して送信できます。
|
||||
|
||||
<img src="/img/tutorial/websockets/image02.png">
|
||||
|
||||
そして、 WebSocketを使用した**FastAPI**アプリケーションが応答します。
|
||||
|
||||
<img src="/img/tutorial/websockets/image03.png">
|
||||
|
||||
複数のメッセージを送信(および受信)できます。
|
||||
|
||||
<img src="/img/tutorial/websockets/image04.png">
|
||||
|
||||
そして、これらの通信はすべて同じWebSocket接続を使用します。
|
||||
|
||||
## 依存関係
|
||||
|
||||
WebSocketエンドポイントでは、`fastapi` から以下をインポートして使用できます。
|
||||
|
||||
* `Depends`
|
||||
* `Security`
|
||||
* `Cookie`
|
||||
* `Header`
|
||||
* `Path`
|
||||
* `Query`
|
||||
|
||||
これらは、他のFastAPI エンドポイント/*path operation* の場合と同じように機能します。
|
||||
|
||||
{* ../../docs_src/websockets/tutorial002.py hl[58:65,68:83] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
WebSocket で `HTTPException` を発生させることはあまり意味がありません。したがって、WebSocketの接続を直接閉じる方がよいでしょう。
|
||||
|
||||
クロージングコードは、<a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">仕様で定義された有効なコード</a>の中から使用することができます。
|
||||
|
||||
将来的には、どこからでも `raise` できる `WebSocketException` が用意され、専用の例外ハンドラを追加できるようになる予定です。これは、Starlette の <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> に依存するものです。
|
||||
|
||||
///
|
||||
|
||||
### 依存関係を用いてWebSocketsを試してみる
|
||||
|
||||
ファイル名が `main.py` である場合、以下の方法でアプリケーションを実行します。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
ブラウザで <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a> を開きます。
|
||||
|
||||
クライアントが設定できる項目は以下の通りです。
|
||||
|
||||
* パスで使用される「Item ID」
|
||||
* クエリパラメータとして使用される「Token」
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
クエリ `token` は依存パッケージによって処理されることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
これにより、WebSocketに接続してメッセージを送受信できます。
|
||||
|
||||
<img src="/img/tutorial/websockets/image05.png">
|
||||
|
||||
## 切断や複数クライアントへの対応
|
||||
|
||||
WebSocket接続が閉じられると、 `await websocket.receive_text()` は例外 `WebSocketDisconnect` を発生させ、この例のようにキャッチして処理することができます。
|
||||
|
||||
{* ../../docs_src/websockets/tutorial003.py hl[81:83] *}
|
||||
|
||||
試してみるには、
|
||||
|
||||
* いくつかのブラウザタブでアプリを開きます。
|
||||
* それらのタブでメッセージを記入してください。
|
||||
* そして、タブのうち1つを閉じてください。
|
||||
|
||||
これにより例外 `WebSocketDisconnect` が発生し、他のすべてのクライアントは次のようなメッセージを受信します。
|
||||
|
||||
```
|
||||
Client #1596980209979 left the chat
|
||||
```
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
上記のアプリは、複数の WebSocket 接続に対してメッセージを処理し、ブロードキャストする方法を示すための最小限のシンプルな例です。
|
||||
|
||||
しかし、すべての接続がメモリ内の単一のリストで処理されるため、プロセスの実行中にのみ機能し、単一のプロセスでのみ機能することに注意してください。
|
||||
|
||||
もしFastAPIと簡単に統合できて、RedisやPostgreSQLなどでサポートされている、より堅牢なものが必要なら、<a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a> を確認してください。
|
||||
|
||||
///
|
||||
|
||||
## その他のドキュメント
|
||||
|
||||
オプションの詳細については、Starletteのドキュメントを確認してください。
|
||||
|
||||
* <a href="https://www.starlette.dev/websockets/" class="external-link" target="_blank"> `WebSocket` クラス</a>
|
||||
* <a href="https://www.starlette.dev/endpoints/#websocketendpoint" class="external-link" target="_blank">クラスベースのWebSocket処理</a>
|
||||
@@ -0,0 +1,488 @@
|
||||
# 代替ツールから受けたインスピレーションと比較
|
||||
|
||||
何が**FastAPI**にインスピレーションを与えたのか、他の代替ツールと比較してどうか、そしてそこから何を学んだのかについて。
|
||||
|
||||
## はじめに
|
||||
|
||||
**FastAPI**は、代替ツールのこれまでの働きがなければ存在しなかったでしょう。
|
||||
|
||||
以前に作られた多くのツールが、作成における刺激として役立ってきました。
|
||||
|
||||
私は数年前から新しいフレームワークの作成を避けてきました。まず、**FastAPI**でカバーされているすべての機能を、さまざまなフレームワーク、プラグイン、ツールを使って解決しようとしました。
|
||||
|
||||
しかし、その時点では、これらの機能をすべて提供し、以前のツールから優れたアイデアを取り入れ、可能な限り最高の方法でそれらを組み合わせ、それまで利用できなかった言語機能 (Python 3.6以降の型ヒント) を利用したものを作る以外に選択肢はありませんでした。
|
||||
|
||||
## 以前のツール
|
||||
|
||||
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a>
|
||||
|
||||
Pythonのフレームワークの中で最もポピュラーで、広く信頼されています。Instagramのようなシステムの構築に使われています。
|
||||
|
||||
リレーショナルデータベース (MySQLやPostgreSQLなど) と比較的強固に結合されているので、NoSQLデータベース (Couchbase、MongoDB、Cassandraなど) をメインに利用することは簡単ではありません。
|
||||
|
||||
バックエンドでHTMLを生成するために作られたものであり、現代的なフロントエンド (ReactやVue.js、Angularなど) や、他のシステム (IoTデバイスなど) と通信するAPIを構築するために作られたものではありません。
|
||||
|
||||
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a>
|
||||
|
||||
Django REST Frameworkは、Djangoを下敷きにしてWeb APIを構築する柔軟なツールキットとして、APIの機能を向上させるために作られました。
|
||||
|
||||
Mozilla、Red Hat、Eventbrite など多くの企業で利用されています。
|
||||
|
||||
これは**自動的なAPIドキュメント生成**の最初の例であり、これは**FastAPI**に向けた「調査」を触発した最初のアイデアの一つでした。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
Django REST Framework は Tom Christie によって作成されました。StarletteとUvicornの生みの親であり、**FastAPI**のベースとなっています。
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
自動でAPIドキュメントを生成するWebユーザーインターフェースを持っている点。
|
||||
|
||||
///
|
||||
|
||||
### <a href="http://flask.pocoo.org/" class="external-link" target="_blank">Flask</a>
|
||||
|
||||
Flask は「マイクロフレームワーク」であり、データベースとの統合のようなDjangoがデフォルトで持つ多くの機能は含まれていません。
|
||||
|
||||
このシンプルさと柔軟性により、メインのデータストレージシステムとしてNoSQLデータベースを使用するといったようなことが可能になります。
|
||||
|
||||
非常にシンプルなので、ドキュメントはいくつかの点でやや技術的でありますが、比較的直感的に学ぶことができます。
|
||||
|
||||
また、データベースやユーザ管理、あるいはDjangoにあらかじめ組み込まれている多くの機能を必ずしも必要としないアプリケーションにもよく使われています。これらの機能の多くはプラグインで追加することができます。
|
||||
|
||||
このようにパーツを分離し、必要なものを正確にカバーするために拡張できる「マイクロフレームワーク」であることは、私が残しておきたかった重要な機能でした。
|
||||
|
||||
Flaskのシンプルさを考えると、APIを構築するのに適しているように思えました。次に見つけるべきは、Flask 用の「Django REST Framework」でした。
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
マイクロフレームワークであること。ツールやパーツを目的に合うように簡単に組み合わせられる点。
|
||||
|
||||
シンプルで簡単なルーティングの仕組みを持っている点。
|
||||
|
||||
///
|
||||
|
||||
### <a href="http://docs.python-requests.org" class="external-link" target="_blank">Requests</a>
|
||||
|
||||
**FastAPI**は実際には**Requests**の代替ではありません。それらのスコープは大きく異なります。
|
||||
|
||||
実際にはFastAPIアプリケーションの*内部*でRequestsを使用するのが一般的です。
|
||||
|
||||
しかし、FastAPIはRequestsからかなりのインスピレーションを得ています。
|
||||
|
||||
**Requests**は (クライアントとして) APIと*通信*するためのライブラリであり、**FastAPI**は (サーバーとして) APIを*構築*するためのライブラリです。
|
||||
|
||||
これらは多かれ少なかれ両端にあり、お互いを補完し合っています。
|
||||
|
||||
Requestsは非常にシンプルかつ直感的なデザインで使いやすく、適切なデフォルト値を設定しています。しかし同時に、非常に強力でカスタマイズも可能です。
|
||||
|
||||
公式サイトで以下のように言われているのは、それが理由です。
|
||||
|
||||
> Requestsは今までで最もダウンロードされたPythonパッケージである
|
||||
|
||||
使い方はとても簡単です。例えば、`GET`リクエストを実行するには、このように書けば良いです:
|
||||
|
||||
```Python
|
||||
response = requests.get("http://example.com/some/url")
|
||||
```
|
||||
|
||||
対応するFastAPIのパスオペレーションはこのようになります:
|
||||
|
||||
```Python hl_lines="1"
|
||||
@app.get("/some/url")
|
||||
def read_url():
|
||||
return {"message": "Hello World"}
|
||||
```
|
||||
|
||||
`requests.get(...)` と`@app.get(...)` には類似点が見受けられます。
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
* シンプルで直感的なAPIを持っている点。
|
||||
* HTTPメソッド名を直接利用し、単純で直感的である。
|
||||
* 適切なデフォルト値を持ちつつ、強力なカスタマイズ性を持っている。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a>
|
||||
|
||||
私がDjango REST Frameworkに求めていた主な機能は、APIの自動的なドキュメント生成でした。
|
||||
|
||||
そして、Swaggerと呼ばれる、 JSON (またはYAML、JSONの拡張版) を使ってAPIをドキュメント化するための標準があることを知りました。
|
||||
|
||||
そして、Swagger API用のWebユーザーインターフェースが既に作成されていました。つまり、API用のSwaggerドキュメントを生成することができれば、このWebユーザーインターフェースを自動的に使用することができるようになります。
|
||||
|
||||
ある時点で、SwaggerはLinux Foundationに寄贈され、OpenAPIに改名されました。
|
||||
|
||||
そのため、バージョン2.0では「Swagger」、バージョン3以上では「OpenAPI」と表記するのが一般的です。
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
独自のスキーマの代わりに、API仕様のオープンな標準を採用しました。
|
||||
|
||||
そして、標準に基づくユーザーインターフェースツールを統合しています。
|
||||
|
||||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>
|
||||
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>
|
||||
|
||||
この二つは人気で安定したものとして選択されましたが、少し検索してみると、 (**FastAPI**と同時に使用できる) OpenAPIのための多くの代替となるツールを見つけることができます。
|
||||
|
||||
///
|
||||
|
||||
### Flask REST フレームワーク
|
||||
|
||||
いくつかのFlask RESTフレームワークがありますが、それらを調査してみたところ、多くのものが不適切な問題が残ったまま、中断されたり放置されていることがわかりました。
|
||||
|
||||
### <a href="https://marshmallow.readthedocs.io/en/3.0/" class="external-link" target="_blank">Marshmallow</a>
|
||||
|
||||
APIシステムで必要とされる主な機能の一つに、コード (Python) からデータを取り出して、ネットワークを介して送れるものに変換するデータの「<abbr title="marshalling, conversion">シリアライゼーション</abbr>」があります。例えば、データベースのデータを含むオブジェクトをJSONオブジェクトに変換したり、`datetime` オブジェクトを文字列に変換するなどです。
|
||||
|
||||
APIが必要とするもう一つの大きな機能はデータのバリデーションであり、特定のパラメータが与えられた場合にデータが有効であることを確認することです。例えば、あるフィールドがランダムな文字列ではなく `int` であることなどです。これは特に受信するデータに対して便利です。
|
||||
|
||||
データバリデーションの仕組みがなければ、すべてのチェックを手作業でコードに実装しなければなりません。
|
||||
|
||||
これらの機能は、Marshmallowが提供するものです。Marshmallowは素晴らしいライブラリで、私も以前に何度も使ったことがあります。
|
||||
|
||||
しかし、それはPythonの型ヒントが存在する前に作られたものです。そのため、すべての<abbr title="データがどのように形成されるべきかの定義">スキーマ</abbr>を定義するためには、Marshmallowが提供する特定のユーティリティやクラスを使用する必要があります。
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
コードで「スキーマ」を定義し、データの型やバリデーションを自動で提供する点。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a>
|
||||
|
||||
APIに求められる他の大きな機能として、<abbr title="Pythonデータの読み込みと変換">受信したリクエストデータのパース</abbr>があります。
|
||||
|
||||
WebargsはFlaskをはじめとするいくつかのフレームワークの上にそれを提供するために作られたツールです。
|
||||
|
||||
データのバリデーションを行うために内部ではMarshmallowを使用しており、同じ開発者によって作られました。
|
||||
|
||||
素晴らしいツールで、私も**FastAPI**を持つ前はよく使っていました。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
Webargsは、Marshmallowと同じ開発者により作られました。
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
受信したデータに対する自動的なバリデーションを持っている点。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a>
|
||||
|
||||
MarshmallowとWebargsはバリデーション、パース、シリアライゼーションをプラグインとして提供しています。
|
||||
|
||||
しかし、ドキュメントはまだ不足しています。そこでAPISpecが作られました。
|
||||
|
||||
これは多くのフレームワーク用のプラグインです (Starlette用のプラグインもあります) 。
|
||||
|
||||
仕組みとしては、各ルーティング関数のdocstringにYAML形式でスキーマの定義を記述します。
|
||||
|
||||
そして、OpenAPIスキーマを生成してくれます。
|
||||
|
||||
Flask, Starlette, Responderなどにおいてはそのように動作します。
|
||||
|
||||
しかし、Pythonの文字列 (大きなYAML) の中に、小さな構文があるという問題があります。
|
||||
|
||||
エディタでは、この問題を解決することはできません。また、パラメータやMarshmallowスキーマを変更したときに、YAMLのdocstringを変更するのを忘れてしまうと、生成されたスキーマが古くなってしまいます。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
APISpecは、Marshmallowと同じ開発者により作成されました。
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
OpenAPIという、APIについてのオープンな標準をサポートしている点。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a>
|
||||
|
||||
Webargs、Marshmallow、APISpecを連携させたFlaskプラグインです。
|
||||
|
||||
WebargsとMarshmallowの情報から、APISpecを使用してOpenAPIスキーマを自動的に生成します。
|
||||
|
||||
これは素晴らしいツールで、非常に過小評価されています。多くの Flask プラグインよりもずっと人気があるべきです。ドキュメントがあまりにも簡潔で抽象的であるからかもしれません。
|
||||
|
||||
これにより、PythonのdocstringにYAML (別の構文) を書く必要がなくなりました。
|
||||
|
||||
Flask、Flask-apispec、Marshmallow、Webargsの組み合わせは、**FastAPI**を構築するまで私のお気に入りのバックエンドスタックでした。
|
||||
|
||||
これを使うことで、いくつかのFlaskフルスタックジェネレータを作成することになりました。これらは私 (といくつかの外部のチーム) が今まで使ってきたメインのスタックです。
|
||||
|
||||
* <a href="https://github.com/tiangolo/full-stack" class="external-link" target="_blank">https://github.com/tiangolo/full-stack</a>
|
||||
* <a href="https://github.com/tiangolo/full-stack-flask-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchbase</a>
|
||||
* <a href="https://github.com/tiangolo/full-stack-flask-couchdb" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchdb</a>
|
||||
|
||||
そして、これらのフルスタックジェネレーターは、[**FastAPI** Project Generators](project-generation.md){.internal-link target=_blank}の元となっていました。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
Flask-apispecはMarshmallowと同じ開発者により作成されました。
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
シリアライゼーションとバリデーションを定義したコードから、OpenAPIスキーマを自動的に生成する点。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a> (と<a href="https://angular.io/" class="external-link" target="_blank">Angular</a>)
|
||||
|
||||
NestJSはAngularにインスパイアされたJavaScript (TypeScript) NodeJSフレームワークで、Pythonですらありません。
|
||||
|
||||
Flask-apispecでできることと多少似たようなことを実現しています。
|
||||
|
||||
Angular 2にインスピレーションを受けた、統合された依存性注入の仕組みを持っています。(私が知っている他の依存性注入の仕組みと同様に) 「injectable」を事前に登録しておく必要があるため、冗長性とコードの繰り返しが発生します。
|
||||
|
||||
パラメータはTypeScriptの型で記述されるので (Pythonの型ヒントに似ています) 、エディタのサポートはとても良いです。
|
||||
|
||||
しかし、TypeScriptのデータはJavaScriptへのコンパイル後には残されないため、バリデーション、シリアライゼーション、ドキュメント化を同時に定義するのに型に頼ることはできません。そのため、バリデーション、シリアライゼーション、スキーマの自動生成を行うためには、多くの場所でデコレータを追加する必要があり、非常に冗長になります。
|
||||
|
||||
入れ子になったモデルをうまく扱えません。そのため、リクエストのJSONボディが内部フィールドを持つJSONオブジェクトで、それが順番にネストされたJSONオブジェクトになっている場合、適切にドキュメント化やバリデーションをすることができません。
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
素晴らしいエディターの補助を得るために、Pythonの型ヒントを利用している点。
|
||||
|
||||
強力な依存性注入の仕組みを持ち、コードの繰り返しを最小化する方法を見つけた点。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a>
|
||||
|
||||
`asyncio`に基づいた、Pythonのフレームワークの中でも非常に高速なものの一つです。Flaskと非常に似た作りになっています。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
Pythonの`asyncio`ループの代わりに、`uvloop`が利用されています。それにより、非常に高速です。
|
||||
|
||||
`Uvicorn`と`Starlette`に明らかなインスピレーションを与えており、それらは現在オープンなベンチマークにおいてSanicより高速です。
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
物凄い性能を出す方法を見つけた点。
|
||||
|
||||
**FastAPI**が、(サードパーティのベンチマークによりテストされた) 最も高速なフレームワークであるStarletteに基づいている理由です。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a>
|
||||
|
||||
Falconはもう一つの高性能Pythonフレームワークで、ミニマムに設計されており、Hugのような他のフレームワークの基盤として動作します。
|
||||
|
||||
Pythonのウェブフレームワーク標準規格 (WSGI) を使用していますが、それは同期的であるためWebSocketなどの利用には対応していません。とはいえ、それでも非常に高い性能を持っています。
|
||||
|
||||
これは、「リクエスト」と「レスポンス」の2つのパラメータを受け取る関数を持つように設計されています。そして、リクエストからデータを「読み込み」、レスポンスにデータを「書き込み」ます。この設計のため、Python標準の型ヒントでリクエストのパラメータやボディを関数の引数として宣言することはできません。
|
||||
|
||||
そのため、データのバリデーション、シリアライゼーション、ドキュメント化は、自動的にできずコードの中で行わなければなりません。あるいは、HugのようにFalconの上にフレームワークとして実装されなければなりません。このような分断は、パラメータとして1つのリクエストオブジェクトと1つのレスポンスオブジェクトを持つというFalconのデザインにインスピレーションを受けた他のフレームワークでも起こります。
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
素晴らしい性能を得るための方法を見つけた点。
|
||||
|
||||
Hug (HugはFalconをベースにしています) と一緒に、**FastAPI**が`response`引数を関数に持つことにインスピレーションを与えました。
|
||||
|
||||
**FastAPI**では任意ですが、ヘッダーやCookieやステータスコードを設定するために利用されています。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a>
|
||||
|
||||
**FastAPI**を構築する最初の段階でMoltenを発見しました。そして、それは非常に似たようなアイデアを持っています。
|
||||
|
||||
* Pythonの型ヒントに基づいている
|
||||
* これらの型から行われるバリデーションとドキュメント化
|
||||
* 依存性注入の仕組み
|
||||
|
||||
Pydanticのようなデータのバリデーション、シリアライゼーション、ドキュメント化をするサードパーティライブラリを使用せず、独自のものを持っています。そのため、これらのデータ型の定義は簡単には再利用できません。
|
||||
|
||||
もう少し冗長な設定が必要になります。また、 (ASGIではなく) WSGIに基づいているので、UvicornやStarletteやSanicのようなツールが提供する高性能を得られるようには設計されていません。
|
||||
|
||||
依存性注入の仕組みは依存性の事前登録が必要で、宣言された型に基づいて依存性が解決されます。そのため、特定の型を提供する「コンポーネント」を複数宣言することはできません。
|
||||
|
||||
ルーティングは一つの場所で宣言され、他の場所で宣言された関数を使用します (エンドポイントを扱う関数のすぐ上に配置できるデコレータを使用するのではなく) 。これはFlask (やStarlette) よりも、Djangoに近いです。これは、比較的緊密に結合されているものをコードの中で分離しています。
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
モデルの属性の「デフォルト」値を使用したデータ型の追加バリデーションを定義します。これはエディタの補助を改善するもので、以前はPydanticでは利用できませんでした。
|
||||
|
||||
同様の方法でのバリデーションの宣言をサポートするよう、Pydanticを部分的にアップデートするインスピーレションを与えました。(現在はこれらの機能は全てPydanticで可能となっています。)
|
||||
|
||||
///
|
||||
|
||||
### <a href="http://www.hug.rest/" class="external-link" target="_blank">Hug</a>
|
||||
|
||||
Hugは、Pythonの型ヒントを利用してAPIパラメータの型宣言を実装した最初のフレームワークの1つです。これは素晴らしいアイデアで、他のツールが同じことをするきっかけとなりました。
|
||||
|
||||
Hugは標準のPython型の代わりにカスタム型を宣言に使用していましたが、それでも大きな進歩でした。
|
||||
|
||||
また、JSONでAPI全体を宣言するカスタムスキーマを生成した最初のフレームワークの1つでもあります。
|
||||
|
||||
OpenAPIやJSON Schemaのような標準に基づいたものではありませんでした。そのため、Swagger UIのような他のツールと統合するのは簡単ではありませんでした。しかし、繰り返しになりますが、これは非常に革新的なアイデアでした。
|
||||
|
||||
同じフレームワークを使ってAPIとCLIを作成できる、面白く珍しい機能を持っています。
|
||||
|
||||
以前のPythonの同期型Webフレームワーク標準 (WSGI) をベースにしているため、Websocketなどは扱えませんが、それでも高性能です。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
HugはTimothy Crosleyにより作成されました。彼は<a href="https://github.com/timothycrosley/isort" class="external-link" target="_blank">`isort`</a>など、Pythonのファイル内のインポートの並び替えを自動的におこうなう素晴らしいツールの開発者です。
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
HugはAPIStarに部分的なインスピレーションを与えており、私が発見した中ではAPIStarと同様に最も期待の持てるツールの一つでした。
|
||||
|
||||
Hugは、**FastAPI**がPythonの型ヒントを用いてパラメータを宣言し自動的にAPIを定義するスキーマを生成することを触発しました。
|
||||
|
||||
Hugは、**FastAPI**がヘッダーやクッキーを設定するために関数に `response`引数を宣言することにインスピレーションを与えました。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5)
|
||||
|
||||
**FastAPI**を構築することを決める直前に、**APIStar**サーバーを見つけました。それは私が探していたものがほぼすべて含まれており、素晴らしいデザインでした。
|
||||
|
||||
これは、私がこれまでに見た中で (NestJSやMoltenの前に) Pythonの型ヒントを使ってパラメータやリクエストを宣言するフレームワークの最初の実装の一つでした。Hugと多かれ少なかれ同時期に見つけました。ただ、APIStarはOpenAPI標準を使っていました。
|
||||
|
||||
いくつかの場所で同じ型ヒントを元に、データの自動バリデーション、データのシリアライゼーション、OpenAPIスキーマの生成を行っていました。
|
||||
|
||||
ボディのスキーマ定義にはPydanticのようなPythonの型ヒントは使われておらず、もう少しMarshmallowに似ていたので、エディタの補助はあまり良くないかもしれませんが、それでもAPIStarは利用可能な最良の選択肢でした。
|
||||
|
||||
当時のベンチマークでは最高のパフォーマンスを発揮していました (Starletteのみが上回っていました) 。
|
||||
|
||||
最初は自動的なAPIのドキュメント化のWeb UIを持っていませんでしたが、Swagger UIを追加できることはわかっていました。
|
||||
|
||||
依存性注入の仕組みを持っていました。上記で説明した他のツールのようにコンポーネントの事前登録が必要でした。しかし、それでも素晴らしい機能でした。
|
||||
|
||||
セキュリティの統合がなかったので、Flask-apispecを元にしたフルスタックジェネレータにあるすべての機能を置き換えることはできませんでした。私はその機能を追加するプルリクエストを作成するというプロジェクトのバックログを持っていました。
|
||||
|
||||
しかし、その後、プロジェクトの焦点が変わりました。
|
||||
|
||||
制作者はStarletteに集中する必要があったため、APIウェブフレームワークではなくなりました。
|
||||
|
||||
今ではAPIStarはOpenAPI仕様を検証するためのツールセットであり、ウェブフレームワークではありません。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
APIStarはTom Christieにより開発されました。以下の開発者でもあります:
|
||||
|
||||
* Django REST Framework
|
||||
* Starlette (**FastAPI**のベースになっています)
|
||||
* Uvicorn (Starletteや**FastAPI**で利用されています)
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI**へ与えたインスピレーション
|
||||
|
||||
存在そのもの。
|
||||
|
||||
複数の機能 (データのバリデーション、シリアライゼーション、ドキュメント化) を同じPython型で宣言し、同時に優れたエディタの補助を提供するというアイデアは、私にとって素晴らしいアイデアでした。
|
||||
|
||||
そして、長い間同じようなフレームワークを探し、多くの異なる代替ツールをテストした結果、APIStarが最良の選択肢となりました。
|
||||
|
||||
その後、APIStarはサーバーとして存在しなくなり、Starletteが作られ、そのようなシステムのための新しくより良い基盤となりました。これが**FastAPI**を構築するための最終的なインスピレーションでした。
|
||||
|
||||
私は、これまでのツールから学んだことをもとに、機能や型システムなどの部分を改善・拡充しながら、**FastAPI**をAPIStarの「精神的な後継者」と考えています。
|
||||
|
||||
///
|
||||
|
||||
## **FastAPI**が利用しているもの
|
||||
|
||||
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>
|
||||
|
||||
Pydanticは、Pythonの型ヒントを元にデータのバリデーション、シリアライゼーション、 (JSON Schemaを使用した) ドキュメントを定義するライブラリです。
|
||||
|
||||
そのため、非常に直感的です。
|
||||
|
||||
Marshmallowに匹敵しますが、ベンチマークではMarshmallowよりも高速です。また、Pythonの型ヒントを元にしているので、エディタの補助が素晴らしいです。
|
||||
|
||||
/// check | **FastAPI**での使用用途
|
||||
|
||||
データのバリデーション、データのシリアライゼーション、自動的なモデルの (JSON Schemaに基づいた) ドキュメント化の全てを扱えます。
|
||||
|
||||
**FastAPI**はJSON SchemaのデータをOpenAPIに利用します。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a>
|
||||
|
||||
Starletteは、軽量な<abbr title="非同期Python webを構築するための新標準">ASGI</abbr>フレームワーク/ツールキットで、高性能な非同期サービスの構築に最適です。
|
||||
|
||||
非常にシンプルで直感的です。簡単に拡張できるように設計されており、モジュール化されたコンポーネントを持っています。
|
||||
|
||||
以下のような特徴があります。
|
||||
|
||||
* 非常に感動的な性能。
|
||||
* WebSocketのサポート。
|
||||
* GraphQLのサポート。
|
||||
* インプロセスのバックグラウンドタスク。
|
||||
* 起動およびシャットダウンイベント。
|
||||
* requestsに基づいて構築されたテストクライアント。
|
||||
* CORS、GZip、静的ファイル、ストリーミング応答。
|
||||
* セッションとクッキーのサポート。
|
||||
* 100%のテストカバレッジ。
|
||||
* 100%の型注釈付きコードベース。
|
||||
* ハードな依存関係はない。
|
||||
|
||||
Starletteは、現在テストされているPythonフレームワークの中で最も速いフレームワークです。フレームワークではなくサーバーであるUvicornだけが上回っています。
|
||||
|
||||
Starletteは基本的なWebマイクロフレームワークの機能をすべて提供します。
|
||||
|
||||
しかし、自動的なデータバリデーション、シリアライゼーション、ドキュメント化は提供していません。
|
||||
|
||||
これは **FastAPI** が追加する主な機能の一つで、すべての機能は Pythonの型ヒントに基づいています (Pydanticを使用しています) 。これに加えて、依存性注入の仕組み、セキュリティユーティリティ、OpenAPIスキーマ生成などがあります。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
ASGIはDjangoのコアチームメンバーにより開発された新しい「標準」です。まだ「Pythonの標準 (PEP) 」ではありませんが、現在そうなるように進めています。
|
||||
|
||||
しかしながら、いくつかのツールにおいてすでに「標準」として利用されています。このことは互換性を大きく改善するもので、Uvicornから他のASGIサーバー (DaphneやHypercorn) に乗り換えることができたり、あなたが`python-socketio`のようなASGI互換のツールを追加することもできます。
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI**での使用用途
|
||||
|
||||
webに関するコアな部分を全て扱います。その上に機能を追加します。
|
||||
|
||||
`FastAPI`クラスそのものは、`Starlette`クラスを直接継承しています。
|
||||
|
||||
基本的にはStarletteの強化版であるため、Starletteで可能なことは**FastAPI**で直接可能です。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>
|
||||
|
||||
Uvicornは非常に高速なASGIサーバーで、uvloopとhttptoolsにより構成されています。
|
||||
|
||||
ウェブフレームワークではなくサーバーです。例えば、パスルーティングのツールは提供していません。それらは、Starlette (や**FastAPI**) のようなフレームワークがその上で提供するものです。
|
||||
|
||||
Starletteや**FastAPI**のサーバーとして推奨されています。
|
||||
|
||||
/// check | **FastAPI**が推奨する理由
|
||||
|
||||
**FastAPI**アプリケーションを実行するメインのウェブサーバーである点。
|
||||
|
||||
Gunicornと組み合わせることで、非同期でマルチプロセスなサーバーを持つことがきます。
|
||||
|
||||
詳細は[デプロイ](deployment/index.md){.internal-link target=_blank}の項目で確認してください。
|
||||
|
||||
///
|
||||
|
||||
## ベンチマーク と スピード
|
||||
|
||||
Uvicorn、Starlette、FastAPIの違いを理解、比較、確認するには、[ベンチマーク](benchmarks.md){.internal-link target=_blank}を確認してください。
|
||||
@@ -0,0 +1,399 @@
|
||||
# 並行処理と async / await
|
||||
|
||||
*path operation 関数*のための `async def` に関する詳細と非同期 (asynchronous) コード、並行処理 (Concurrency)、そして、並列処理 (Parallelism) の背景について。
|
||||
|
||||
## 急いでいますか?
|
||||
|
||||
<abbr title="too long; didn't read (長すぎて読めない人のための要約という意味のスラング)"><strong>TL;DR:</strong></abbr>
|
||||
|
||||
次のような、`await` を使用して呼び出すべきサードパーティライブラリを使用している場合:
|
||||
|
||||
```Python
|
||||
results = await some_library()
|
||||
```
|
||||
|
||||
以下の様に `async def` を使用して*path operation 関数*を宣言します。
|
||||
|
||||
```Python hl_lines="2"
|
||||
@app.get('/')
|
||||
async def read_results():
|
||||
results = await some_library()
|
||||
return results
|
||||
```
|
||||
|
||||
/// note | 備考
|
||||
|
||||
`async def` を使用して作成された関数の内部でしか `await` は使用できません。
|
||||
|
||||
///
|
||||
|
||||
---
|
||||
|
||||
データベース、API、ファイルシステムなどと通信し、`await` の使用をサポートしていないサードパーティライブラリ (現在のほとんどのデータベースライブラリに当てはまります) を使用している場合、次の様に、単に `def` を使用して通常通り *path operation 関数* を宣言してください:
|
||||
|
||||
```Python hl_lines="2"
|
||||
@app.get('/')
|
||||
def results():
|
||||
results = some_library()
|
||||
return results
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
アプリケーションが (どういうわけか) 他の何とも通信せず、応答を待つ必要がない場合は、`async def` を使用して下さい。
|
||||
|
||||
---
|
||||
|
||||
よく分からない場合は、通常の `def` を使用して下さい。
|
||||
|
||||
---
|
||||
|
||||
**備考**: *path operation 関数*に必要なだけ `def` と `async def` を混在させ、それぞれに最適なオプションを使用して定義できます。それに応じてFastAPIは正しい処理を行います。
|
||||
|
||||
とにかく、上記のいずれの場合でもFastAPIは非同期で動作し、非常に高速です。
|
||||
|
||||
しかし、上記のステップに従うことで、パフォーマンスの最適化を行えます。
|
||||
|
||||
## 技術詳細
|
||||
|
||||
現代版のPythonは「**非同期コード**」を、「**コルーチン**」と称されるものを利用してサポートしています。これは **`async` と `await`** 構文を用います。
|
||||
|
||||
次のセクションで、フレーズ内のパーツを順に見ていきましょう:
|
||||
|
||||
* **非同期コード**
|
||||
* **`async` と `await`**
|
||||
* **コルーチン**
|
||||
|
||||
## 非同期コード
|
||||
|
||||
非同期コードとは、言語💬がコード内のどこかで、コンピュータ/プログラム🤖に *他の何か* がどこか別の箇所で終了するのを待つように伝える手段を持っていることを意味します。*他の何か* は「遅いファイル📝」と呼ばれているとしましょう.
|
||||
|
||||
したがって、コンピュータは「遅いファイル📝」が終了するまで、他の処理ができます。
|
||||
|
||||
コンピュータ/プログラム🤖は再び待機する機会があるときや、その時点で行っていたすべての作業が完了するたびに戻ってきます。そして、必要な処理をしながら、コンピュータ/プログラム🤖が待っていた処理のどれかが終わっているかどうか確認します。
|
||||
|
||||
次に、それ🤖が最初のタスク (要するに、先程の「遅いファイル📝」)を終わらせて、そのタスクの結果を使う必要がある処理を続けます。
|
||||
|
||||
この「他の何かを待つ」とは、通常以下の様なものを待つような (プロセッサとRAMメモリの速度に比べて) 相対的に「遅い」<abbr title="インプットとアウトプット">I/O</abbr> 操作を指します:
|
||||
|
||||
* ネットワーク経由でクライアントから送信されるデータ
|
||||
* ネットワーク経由でクライアントが受信する、プログラムから送信されたデータ
|
||||
* システムによって読み取られ、プログラムに渡されるディスク内のファイル内容
|
||||
* プログラムがシステムに渡して、ディスクに書き込む内容
|
||||
* リモートAPI操作
|
||||
* データベース操作の完了
|
||||
* データベースクエリが結果を返すこと
|
||||
* など。
|
||||
|
||||
実行時間のほとんどが<abbr title="インプットとアウトプット">I/O</abbr> 操作の待ち時間が占めるため、このような操作を「I/O バウンド」操作と言います。
|
||||
|
||||
コンピュータ/プログラムがこのような遅いタスクと「同期 (タスクの結果を取得して作業を続行するために、何もせずに、タスクが完了する瞬間を正確に待つ)」する必要がないため、「非同期」と呼ばれます。
|
||||
|
||||
その代わりに、「非同期」システムであることにより、いったん終了すると、タスクは、コンピュータ/プログラムが既に開始した処理がすべて完了するのをほんの少し (数マイクロ秒) 待って、結果を受け取りに戻ってきます。そして、処理を継続します。
|
||||
|
||||
「同期」の場合 (「非同期」とは異なり)、「シーケンシャル」という用語もよく使用されます。これは、コンピュータ/プログラムがすべてのステップを (待機が伴う場合でも別のタスクに切り替えることなく) 順番に実行するためです。
|
||||
|
||||
### 並行処理とハンバーガー
|
||||
|
||||
上記の**非同期**コードのアイデアは、**「並行処理」**と呼ばれることもあります。 **「並列処理」**とは異なります。
|
||||
|
||||
**並行処理**と**並列処理**はどちらも「多かれ少なかれ同時に発生するさまざまなこと」に関連しています。
|
||||
|
||||
ただし、*並行処理*と*並列処理*の詳細はまったく異なります。
|
||||
|
||||
違いを確認するには、ハンバーガーに関する次の物語を想像してみてください:
|
||||
|
||||
### 並行ハンバーガー
|
||||
|
||||
ファストフード🍔を食べようと、好きな人😍とレジに並んでおり、レジ係💁があなたの前にいる人達の注文を受けつけています。
|
||||
|
||||
それからあなたの番になり、好きな人😍と自分のために、2つの非常に豪華なハンバーガー🍔を注文します。
|
||||
|
||||
料金を支払います💸。
|
||||
|
||||
レジ係💁はキッチンの男👨🍳に向かって、あなたのハンバーガー🍔を準備しなければならないと伝えるために何か言いました (彼は現在、前のお客さんの商品を準備していますが)。
|
||||
|
||||
レジ係💁はあなたに番号札を渡します。
|
||||
|
||||
待っている間、好きな人😍と一緒にテーブルを選んで座り、好きな人😍と長い間話をします (注文したハンバーガーは非常に豪華で、準備に少し時間がかかるので✨🍔✨)。
|
||||
|
||||
ハンバーガー🍔を待ちながら好きな人😍とテーブルに座っている間、あなたの好きな人がなんて素晴らしく、かわいくて頭がいいんだと✨😍✨惚れ惚れしながら時間を費やすことができます。
|
||||
|
||||
好きな人😍と話しながら待っている間、ときどき、カウンターに表示されている番号をチェックして、自分の番かどうかを確認します。
|
||||
|
||||
その後、ついにあなたの番になりました。カウンターに行き、ハンバーガー🍔を手に入れてテーブルに戻ります。
|
||||
|
||||
あなたとあなたの好きな人😍はハンバーガー🍔を食べて、楽しい時間を過ごします✨。
|
||||
|
||||
---
|
||||
|
||||
上記のストーリーで、あなたがコンピュータ/プログラム🤖だと想像してみてください。
|
||||
|
||||
列にいる間、あなたはアイドル状態です😴。何も「生産的」なことをせず、ただ自分の番を待っています。しかし、レジ係💁は注文を受け取るだけなので (商品の準備をしているわけではない)、列は高速です。したがって、何も問題ありません。
|
||||
|
||||
それから、あなたの番になったら、実に「生産的な」作業を行います🤓、メニューを確認し、欲しいものを決め、好きな人😍の欲しいものを聞き、料金を支払い💸、現金またはカードを正しく渡したか確認し、正しく清算されたことを確認し、注文が正しく通っているかなどを確認します。
|
||||
|
||||
しかし、ハンバーガー🍔をまだできていないので、ハンバーガーの準備ができるまで待機🕙する必要があるため、レジ係💁との作業は「一時停止⏸」になります。
|
||||
|
||||
しかし、カウンターから離れて、番号札を持ってテーブルに座っているときは、注意を好きな人😍に切り替えて🔀、その上で「仕事⏯🤓」を行なえます。その後、好きな人😍といちゃつくかのような、非常に「生産的な🤓」ことを再び行います。
|
||||
|
||||
次に、レジ係💁は、「ハンバーガーの準備ができました🍔」と言って、カウンターのディスプレイに番号を表示しますが、表示番号があなたの番号に変わっても、すぐに狂ったように飛んで行くようなことはありません。あなたは自分の番号札を持っていって、他の人も自分の番号札があるので、あなたのハンバーガー🍔を盗む人がいないことは知っています。
|
||||
|
||||
なので、あなたは好きな人😍が話し終えるのを待って (現在の仕事⏯ / 処理中のタスクを終了します🤓)、優しく微笑んで、ハンバーガーを貰ってくるねと言います⏸。
|
||||
|
||||
次に、カウンターへ、いまから完了する最初のタスク⏯へ向かい、ハンバーガー🍔を受け取り、感謝の意を表して、テーブルに持っていきます。これで、カウンターとのやり取りのステップ/タスクが完了しました⏹。これにより、「ハンバーガーを食べる🔀⏯」という新しいタスクが作成されます。しかし、前の「ハンバーガーを取得する」というタスクは終了しました⏹。
|
||||
|
||||
### 並列ハンバーガー
|
||||
|
||||
これらが「並行ハンバーガー」ではなく、「並列ハンバーガー」であるとしましょう。
|
||||
|
||||
あなたは好きな人😍と並列ファストフード🍔を買おうとしています。
|
||||
|
||||
列に並んでいますが、何人かの料理人兼、レジ係 (8人としましょう) 👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳があなたの前にいる人達の注文を受けつけています。
|
||||
|
||||
8人のレジ係がそれぞれ自分で注文を受けるや否や、次の注文を受ける前にハンバーガーを準備するので、あなたの前の人達はカウンターを離れずに、ハンバーガー🍔ができるのを待っています🕙。
|
||||
|
||||
それからいよいよあなたの番になり、好きな人😍と自分のために、2つの非常に豪華なハンバーガー🍔を注文します。
|
||||
|
||||
料金を支払います💸。
|
||||
|
||||
レジ係はキッチンに行きます👨🍳。
|
||||
|
||||
あなたはカウンターの前に立って待ちます🕙。番号札がないので誰もあなたよりも先にハンバーガー🍔を取らないようにします。
|
||||
|
||||
あなたと好きな人😍は忙しいので、誰もあなたの前に来させませんし、あなたのハンバーガーが到着したとき🕙に誰にも取ることを許しません。あなたは好きな人に注意を払えません😞。
|
||||
|
||||
これは「同期」作業であり、レジ係/料理人👨🍳と「同期」します。レジ係/料理人👨🍳がハンバーガー🍔を完成させてあなたに渡すまで待つ🕙必要があり、ちょうどその完成の瞬間にそこにいる必要があります。そうでなければ、他の誰かに取られるかもしれません。
|
||||
|
||||
その後、カウンターの前で長い時間待ってから🕙、ついにレジ係/料理人👨🍳がハンバーガー🍔を渡しに戻ってきます。
|
||||
|
||||
ハンバーガー🍔を取り、好きな人😍とテーブルに行きます。
|
||||
|
||||
ただ食べるだけ、それでおしまいです。🍔⏹。
|
||||
|
||||
ほとんどの時間、カウンターの前で待つのに費やされていたので🕙、あまり話したりいちゃつくことはありませんでした😞。
|
||||
|
||||
---
|
||||
|
||||
この並列ハンバーガーのシナリオでは、あなたは2つのプロセッサを備えたコンピュータ/プログラム🤖 (あなたとあなたの好きな人😍) であり、両方とも待機🕙していて、彼らは「カウンターで待機🕙」することに専念しています⏯。
|
||||
|
||||
ファストフード店には8つのプロセッサ (レジ係/料理人) 👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳があります。一方、並行ハンバーガー店には2人 (レジ係と料理人) 💁👨🍳しかいなかったかもしれません。
|
||||
|
||||
しかし、それでも、最終的な体験は最高ではありません😞。
|
||||
|
||||
---
|
||||
|
||||
これは、ハンバーガー🍔の話と同等な話になります。
|
||||
|
||||
より「現実的な」例として、銀行を想像してみてください。
|
||||
|
||||
最近まで、ほとんどの銀行は複数の窓口👨💼👨💼👨💼👨💼に、行列🕙🕙🕙🕙🕙🕙🕙🕙ができていました。
|
||||
|
||||
すべての窓口で、次々と、一人の客とすべての作業を行います👨💼⏯.
|
||||
|
||||
その上、長時間、列に並ばなければいけません🕙。そうしないと、順番が回ってきません。
|
||||
|
||||
銀行🏦での用事にあなたの好きな人😍を連れて行きたくはないでしょう。
|
||||
|
||||
### ハンバーガーのまとめ
|
||||
|
||||
この「好きな人とのファストフードハンバーガー」のシナリオでは、待機🕙が多いため、並行システム⏸🔀⏯を使用する方がはるかに理にかなっています。
|
||||
|
||||
これは、ほとんどのWebアプリケーションに当てはまります。
|
||||
|
||||
多くのユーザーがいますが、サーバーは、あまり強くない回線でのリクエストの送信を待機🕙しています。
|
||||
|
||||
そして、レスポンスが返ってくるのをもう一度待機🕙します。
|
||||
|
||||
この「待機🕙」はマイクロ秒単位ですが、それでも、すべて合算すると、最終的にはかなり待機することになります。
|
||||
|
||||
これが、Web APIへの非同期⏸🔀⏯コードの利用が理にかなっている理由です。
|
||||
|
||||
ほとんどの既存の人気のあるPythonフレームワーク (FlaskやDjangoを含む) は、Pythonの新しい非同期機能ができる前に作成されました。したがって、それらをデプロイする方法は、並列実行と、新機能ほど強力ではない古い形式の非同期実行をサポートします。
|
||||
|
||||
しかし、WebSocketのサポートを追加するために、非同期Web Python (ASGI) の主な仕様はDjangoで開発されました。
|
||||
|
||||
そのような非同期性がNodeJSを人気にした理由です (NodeJSは並列ではありませんが)。そして、プログラミング言語としてのGoの強みでもあります。
|
||||
|
||||
そして、それは**FastAPI**で得られるパフォーマンスと同じレベルです。
|
||||
|
||||
また、並列処理と非同期処理を同時に実行できるため、テスト済みのほとんどのNodeJSフレームワークよりも高く、Goと同等のパフォーマンスが得られます。Goは、Cに近いコンパイル言語です <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(Starletteに感謝します)</a>。
|
||||
|
||||
### 並行は並列よりも優れていますか?
|
||||
|
||||
いや!それはこの話の教訓ではありません。
|
||||
|
||||
並行処理は並列処理とは異なります。多くの待機を伴う**特定の**シナリオに適しています。そのため、一般に、Webアプリケーション開発では並列処理よりもはるかに優れています。しかし、すべてに対してより良いというわけではありません。
|
||||
|
||||
なので、バランスをとるために、次の物語を想像して下さい:
|
||||
|
||||
> あなたは大きくて汚れた家を掃除する必要があります。
|
||||
|
||||
*はい、以上です*。
|
||||
|
||||
---
|
||||
|
||||
待機🕙せず、家の中の複数の場所でたくさんの仕事をするだけです。
|
||||
|
||||
あなたはハンバーガーの例のように、最初はリビングルーム、次にキッチンのように順番にやっていくことができますが、何かを待機🕙しているわけではなく、ただひたすらに掃除をするだけで、順番は何にも影響しません。
|
||||
|
||||
順番の有無に関係なく (並行に) 同じ時間がかかり、同じ量の作業が行われることになるでしょう。
|
||||
|
||||
しかし、この場合、8人の元レジ係/料理人/現役清掃員👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳👨🍳を手配できて、それぞれ (さらにあなたも) が家の別々の場所を掃除できれば、追加の助けを借りて、すべての作業を**並列**に行い、はるかに早く終了できるでしょう。
|
||||
|
||||
このシナリオでは、清掃員 (あなたを含む) のそれぞれがプロセッサとなり、それぞれの役割を果たします。
|
||||
|
||||
また、実行時間のほとんどは (待機ではなく) 実際の作業に費やされ、コンピュータでの作業は<abbr title="Central Processing Unit">CPU</abbr>によって行われます。これらの問題は「CPUバウンド」と言います。
|
||||
|
||||
---
|
||||
|
||||
CPUバウンド操作の一般的な例は、複雑な数学処理が必要なものです。
|
||||
|
||||
例えば:
|
||||
|
||||
* **オーディオ** や **画像処理**。
|
||||
* **コンピュータビジョン**: 画像は数百万のピクセルで構成され、各ピクセルには3つの値/色があり、通常、これらのピクセルで何かを同時に計算する必要がある処理。
|
||||
* **機械学習**: 通常、多くの「行列」と「ベクトル」の乗算が必要です。巨大なスプレッドシートに数字を入れて、それを同時に全部掛け合わせることを考えてみてください。
|
||||
* **ディープラーニング**: これは機械学習のサブフィールドであるため、同じことが当てはまります。乗算する数字がある単一のスプレッドシートではなく、それらの膨大な集合で、多くの場合、それらのモデルを構築および/または使用するために特別なプロセッサを使用します。
|
||||
|
||||
### 並行処理 + 並列処理: Web + 機械学習
|
||||
|
||||
**FastAPI**を使用すると、Web開発で非常に一般的な並行処理 (NodeJSの主な魅力と同じもの) を利用できます。
|
||||
|
||||
ただし、機械学習システムのような **CPUバウンド** ワークロードに対して、並列処理とマルチプロセッシング (複数のプロセスが並列で実行される) の利点を活用することもできます。
|
||||
|
||||
さらに、Pythonが**データサイエンス**、機械学習、特にディープラーニングの主要言語であるという単純な事実により、FastAPIはデータサイエンス/機械学習のWeb APIおよびアプリケーション (他の多くのアプリケーションとの) に非常によく適合しています。
|
||||
|
||||
本番環境でこの並列処理を実現する方法については、[デプロイ](deployment/index.md){.internal-link target=_blank}に関するセクションを参照してください。
|
||||
|
||||
## `async` と `await`
|
||||
|
||||
現代的なバージョンのPythonには、非同期コードを定義する非常に直感的な方法があります。これにより、通常の「シーケンシャル」コードのように見え、適切なタイミングで「待機」します。
|
||||
|
||||
結果を返す前に待機する必要があり、これらの新しいPython機能をサポートする操作がある場合は、次のようにコーディングできます。
|
||||
|
||||
```Python
|
||||
burgers = await get_burgers(2)
|
||||
```
|
||||
|
||||
カギは `await` です。結果を `burgers`に保存する前に、`get_burgers(2)`の処理🕙の完了を待つ⏸必要があることをPythonに伝えます。これでPythonは、その間に (別のリクエストを受信するなど) 何か他のことができる🔀⏯ことを知ります。
|
||||
|
||||
`await` が機能するためには、非同期処理をサポートする関数内にある必要があります。これは、`async def` で関数を宣言するだけでよいです:
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def get_burgers(number: int):
|
||||
# ハンバーガーを作成するために非同期処理を実行
|
||||
return burgers
|
||||
```
|
||||
|
||||
...`def` のかわりに:
|
||||
|
||||
```Python hl_lines="2"
|
||||
# 非同期ではない
|
||||
def get_sequential_burgers(number: int):
|
||||
# ハンバーガーを作成するためにシーケンシャルな処理を実行
|
||||
return burgers
|
||||
```
|
||||
`async def` を使用すると、Pythonにその関数内で `await` 式 (その関数の実行を「一時停止⏸」し、結果が戻るまで他の何かを実行🔀する) を認識しなければならないと伝えることができます。
|
||||
|
||||
`async def` 関数を呼び出すときは、「await」しなければなりません。したがって、これは機能しません:
|
||||
|
||||
```Python
|
||||
# get_burgersはasync defで定義されているので動作しない
|
||||
burgers = get_burgers(2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
したがって、 `await` で呼び出すことができるライブラリを使用している場合は、次のように `async def` を使用して、それを使用する*path operation 関数*を作成する必要があります:
|
||||
|
||||
```Python hl_lines="2-3"
|
||||
@app.get('/burgers')
|
||||
async def read_burgers():
|
||||
burgers = await get_burgers(2)
|
||||
return burgers
|
||||
```
|
||||
|
||||
### より発展的な技術詳細
|
||||
|
||||
`await` は `async def` で定義された関数内でのみ使用できることがわかったかと思います。
|
||||
|
||||
しかし同時に、`async def` で定義された関数は「awaitされる」必要があります。なので、`async def` を持つ関数は、`async def` で定義された関数内でのみ呼び出せます。
|
||||
|
||||
では、このニワトリと卵の問題について、最初の `async` 関数をどのように呼び出すのでしょうか?
|
||||
|
||||
**FastAPI**を使用している場合、その「最初の」関数が*path operation 関数*であり、FastAPIが正しく実行する方法を知っているので、心配する必要はありません。
|
||||
|
||||
しかし、FastAPI以外で `async` / `await` を使用したい場合は、<a href="https://docs.python.org/3/library/asyncio-task.html#coroutine" class="external-link" target="_blank">公式Pythonドキュメントを参照して下さい</a>。
|
||||
|
||||
### 非同期コードの他の形式
|
||||
|
||||
`async` と `await` を使用するスタイルは、この言語では比較的新しいものです。
|
||||
|
||||
非同期コードの操作がはるかに簡単になります。
|
||||
|
||||
等価な (またはほとんど同一の) 構文が、最近のバージョンのJavaScript (ブラウザおよびNodeJS) にも最近組み込まれました。
|
||||
|
||||
しかし、その前は、非同期コードの処理はかなり複雑で難解でした。
|
||||
|
||||
以前のバージョンのPythonでは、スレッドや<a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a>が利用できました。しかし、コードは理解、デバック、そして、考察がはるかに複雑です。
|
||||
|
||||
以前のバージョンのNodeJS / ブラウザJavaScriptでは、「コールバック」を使用していました。これは、「コールバック地獄」につながります。
|
||||
|
||||
## コルーチン
|
||||
|
||||
**コルーチン**は、`async def` 関数によって返されるものを指す非常に洒落た用語です。これは、開始できて、いつか終了する関数のようなものであるが、内部に `await` があるときは内部的に一時停止⏸されることもあるものだとPythonは認識しています。
|
||||
|
||||
`async` と `await` を用いた非同期コードを使用するすべての機能は、「コルーチン」を使用するものとして何度もまとめられています。Goの主要機能である「ゴルーチン」に相当します。
|
||||
|
||||
## まとめ
|
||||
|
||||
上述したフレーズを見てみましょう:
|
||||
|
||||
> 現代版のPythonは「**非同期コード**」を、「**コルーチン**」と称されるものを利用してサポートしています。これは **`async` と `await`** 構文を用います。
|
||||
|
||||
今では、この意味がより理解できるはずです。✨
|
||||
|
||||
(Starletteを介して) FastAPIに力を与えて、印象的なパフォーマンスを実現しているものはこれがすべてです。
|
||||
|
||||
## 非常に発展的な技術的詳細
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
恐らくスキップしても良いでしょう。
|
||||
|
||||
この部分は**FastAPI**の仕組みに関する非常に技術的な詳細です。
|
||||
|
||||
かなりの技術知識 (コルーチン、スレッド、ブロッキングなど) があり、FastAPIが `async def` と通常の `def` をどのように処理するか知りたい場合は、先に進んでください。
|
||||
|
||||
///
|
||||
|
||||
### Path operation 関数
|
||||
|
||||
*path operation 関数*を `async def` の代わりに通常の `def` で宣言すると、(サーバーをブロックするので) 直接呼び出す代わりに外部スレッドプール (awaitされる) で実行されます。
|
||||
|
||||
上記の方法と違った方法の別の非同期フレームワークから来ており、小さなパフォーマンス向上 (約100ナノ秒) のために通常の `def` を使用して些細な演算のみ行う *path operation 関数* を定義するのに慣れている場合は、**FastAPI**ではまったく逆の効果になることに注意してください。このような場合、*path operation 関数* がブロッキング<abbr title="入力/出力: ディスクの読み取りまたは書き込み、ネットワーク通信。">I/O</abbr>を実行しないのであれば、`async def` の使用をお勧めします。
|
||||
|
||||
それでも、どちらの状況でも、**FastAPI**が過去のフレームワークよりも (またはそれに匹敵するほど) [高速になる](index.md#_10){.internal-link target=_blank}可能性があります。
|
||||
|
||||
### 依存関係
|
||||
|
||||
依存関係についても同様です。依存関係が `async def` ではなく標準の `def` 関数である場合、外部スレッドプールで実行されます。
|
||||
|
||||
### サブ依存関係
|
||||
|
||||
(関数定義のパラメーターとして) 相互に必要な複数の依存関係とサブ依存関係を設定できます。一部は `async def` で作成され、他の一部は通常の `def` で作成されます。それでも動作し、通常の `def`で作成されたものは、「awaitされる」代わりに (スレッドプールから) 外部スレッドで呼び出されます。
|
||||
|
||||
### その他のユーティリティ関数
|
||||
|
||||
あなたが直接呼び出すユーティリティ関数は通常の `def` または `async def` で作成でき、FastAPIは呼び出す方法に影響を与えません。
|
||||
|
||||
これは、FastAPIが呼び出す関数と対照的です: *path operation 関数*と依存関係。
|
||||
|
||||
ユーティリティ関数が `def` を使用した通常の関数である場合、スレッドプールではなく直接 (コードで記述したとおりに) 呼び出されます。関数が `async def` を使用して作成されている場合は、呼び出す際に `await` する必要があります。
|
||||
|
||||
---
|
||||
|
||||
繰り返しになりますが、これらは非常に技術的な詳細であり、検索して辿り着いた場合は役立つでしょう。
|
||||
|
||||
それ以外の場合は、上記のセクションのガイドラインで問題ないはずです: <a href="#_1">急いでいますか?</a>。
|
||||
@@ -0,0 +1,34 @@
|
||||
# ベンチマーク
|
||||
|
||||
TechEmpowerの独立したベンチマークでは、Uvicornの下で動作する**FastAPI**アプリケーションは、<a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">利用可能な最速のPythonフレームワークの1つ</a>であり、下回っているのはStarletteとUvicorn自体 (FastAPIによって内部で使用される) のみだと示されています。
|
||||
|
||||
ただし、ベンチマークを確認し、比較する際には下記の内容に気を付けてください。
|
||||
|
||||
## ベンチマークと速度
|
||||
|
||||
ベンチマークを確認する時、異なるツールを同等なものと比較するのが一般的です。
|
||||
|
||||
具体的には、Uvicorn、Starlette、FastAPIを (他の多くのツールと) 比較しました。
|
||||
|
||||
ツールで解決する問題がシンプルなほど、パフォーマンスが向上します。また、ほとんどのベンチマークは、ツールから提供される追加機能をテストしていません。
|
||||
|
||||
階層関係はこのようになります。
|
||||
|
||||
* **Uvicorn**: ASGIサーバー
|
||||
* **Starlette**: (Uvicornを使用) WEBマイクロフレームワーク
|
||||
* **FastAPI**: (Starletteを使用) データバリデーションなどの、APIを構築する追加機能を備えたAPIマイクロフレームワーク
|
||||
|
||||
* **Uvicorn**:
|
||||
* サーバー自体に余分なコードが少ないので、最高のパフォーマンスが得られます。
|
||||
* Uvicornにアプリケーションを直接書くことはできません。つまり、あなたのコードには、Starlette (または** FastAPI **) が提供するコードを、多かれ少なかれ含める必要があります。そうすると、最終的なアプリケーションは、フレームワークを使用してアプリのコードとバグを最小限に抑えた場合と同じオーバーヘッドになります。
|
||||
* もしUvicornを比較する場合は、Daphne、Hypercorn、uWSGIなどのアプリケーションサーバーと比較してください。
|
||||
* **Starlette**:
|
||||
* Uvicornに次ぐ性能を持つでしょう。実際、StarletteはUvicornを使用しています。だから、より多くのコードを実行する必要があり、Uvicornよりも「遅く」なってしまうだけなのです。
|
||||
* しかし、パスベースのルーティングなどのシンプルなWEBアプリケーションを構築する機能を提供します。
|
||||
* もしStarletteを比較する場合は、Sanic、Flask、DjangoなどのWEBフレームワーク (もしくはマイクロフレームワーク) と比較してください。
|
||||
* **FastAPI**:
|
||||
* StarletteがUvicornを使っているのと同じで、**FastAPI**はStarletteを使っており、それより速くできません。
|
||||
* FastAPIはStarletteの上にさらに多くの機能を提供します。データの検証やシリアライゼーションなど、APIを構築する際に常に必要な機能です。また、それを使用することで、自動ドキュメント化を無料で取得できます (ドキュメントは実行中のアプリケーションにオーバーヘッドを追加せず、起動時に生成されます) 。
|
||||
* FastAPIを使用せず、直接Starlette (またはSanic, Flask, Responderなど) を使用した場合、データの検証とシリアライズをすべて自分で実装する必要があります。そのため、最終的なアプリケーションはFastAPIを使用して構築した場合と同じオーバーヘッドが発生します。そして、多くの場合、このデータ検証とシリアライズは、アプリケーションのコードの中で最大の記述量になります。
|
||||
* FastAPIを使用することで、開発時間、バグ、コード行数を節約でき、使用しない場合 (あなたが全ての機能を実装し直した場合) と同じかそれ以上のパフォーマンスを得られます。
|
||||
* もしFastAPIを比較する場合は、Flask-apispec、NestJS、Moltenなどのデータ検証や、シリアライズの機能を提供するWEBフレームワーク (や機能のセット) と比較してください。これらはデータの自動検証や、シリアライズ、ドキュメント化が統合されたフレームワークです。
|
||||
@@ -0,0 +1,335 @@
|
||||
# デプロイメントのコンセプト
|
||||
|
||||
**FastAPI**を用いたアプリケーションをデプロイするとき、もしくはどのようなタイプのWeb APIであっても、おそらく気になるコンセプトがいくつかあります。
|
||||
|
||||
それらを活用することでアプリケーションを**デプロイするための最適な方法**を見つけることができます。
|
||||
|
||||
重要なコンセプトのいくつかを紹介します:
|
||||
|
||||
* セキュリティ - HTTPS
|
||||
* 起動時の実行
|
||||
* 再起動
|
||||
* レプリケーション(実行中のプロセス数)
|
||||
* メモリー
|
||||
* 開始前の事前のステップ
|
||||
|
||||
これらが**デプロイメント**にどのような影響を与えるかを見ていきましょう。
|
||||
|
||||
最終的な目的は、**安全な方法で**APIクライアントに**サービスを提供**し、**中断を回避**するだけでなく、**計算リソース**(例えばリモートサーバー/仮想マシン)を可能な限り効率的に使用することです。🚀
|
||||
|
||||
この章では前述した**コンセプト**についてそれぞれ説明します。
|
||||
|
||||
この説明を通して、普段とは非常に異なる環境や存在しないであろう**将来の**環境に対し、デプロイの方法を決める上で必要な**直感**を与えてくれることを願っています。
|
||||
|
||||
これらのコンセプトを意識することにより、**あなた自身のAPI**をデプロイするための最適な方法を**評価**し、**設計**することができるようになるでしょう。
|
||||
|
||||
次の章では、FastAPIアプリケーションをデプロイするための**具体的なレシピ**を紹介します。
|
||||
|
||||
しかし、今はこれらの重要な**コンセプトに基づくアイデア**を確認しましょう。これらのコンセプトは、他のどのタイプのWeb APIにも当てはまります。💡
|
||||
|
||||
## セキュリティ - HTTPS
|
||||
|
||||
<!-- NOTE: https.md written in Japanese does not exist, so it redirects to English one -->
|
||||
[前チャプターのHTTPSについて](https.md){.internal-link target=_blank}では、HTTPSがどのようにAPIを暗号化するのかについて学びました。
|
||||
|
||||
通常、アプリケーションサーバにとって**外部の**コンポーネントである**TLS Termination Proxy**によって提供されることが一般的です。このプロキシは通信の暗号化を担当します。
|
||||
|
||||
さらにセキュアな通信において、HTTPS証明書の定期的な更新を行いますが、これはTLS Termination Proxyと同じコンポーネントが担当することもあれば、別のコンポーネントが担当することもあります。
|
||||
|
||||
### HTTPS 用ツールの例
|
||||
TLS Termination Proxyとして使用できるツールには以下のようなものがあります:
|
||||
|
||||
* Traefik
|
||||
* 証明書の更新を自動的に処理 ✨
|
||||
* Caddy
|
||||
* 証明書の更新を自動的に処理 ✨
|
||||
* Nginx
|
||||
* 証明書更新のためにCertbotのような外部コンポーネントを使用
|
||||
* HAProxy
|
||||
* 証明書更新のためにCertbotのような外部コンポーネントを使用
|
||||
* Nginx のような Ingress Controller を持つ Kubernetes
|
||||
* 証明書の更新に cert-manager のような外部コンポーネントを使用
|
||||
* クラウド・プロバイダーがサービスの一部として内部的に処理(下記を参照👇)
|
||||
|
||||
もう1つの選択肢は、HTTPSのセットアップを含んだより多くの作業を行う**クラウド・サービス**を利用することです。 このサービスには制限があったり、料金が高くなったりする可能性があります。しかしその場合、TLS Termination Proxyを自分でセットアップする必要はないです。
|
||||
|
||||
次の章で具体例をいくつか紹介します。
|
||||
|
||||
---
|
||||
|
||||
次に考慮すべきコンセプトは、実際のAPIを実行するプログラム(例:Uvicorn)に関連するものすべてです。
|
||||
|
||||
## プログラム と プロセス
|
||||
|
||||
私たちは「**プロセス**」という言葉についてたくさん話すので、その意味や「**プログラム**」という言葉との違いを明確にしておくと便利です。
|
||||
|
||||
### プログラムとは何か
|
||||
|
||||
**プログラム**という言葉は、一般的にいろいろなものを表現するのに使われます:
|
||||
|
||||
* プログラマが書く**コード**、**Pythonファイル**
|
||||
* OSによって実行することができるファイル(例: `python`, `python.exe` or `uvicorn`)
|
||||
* OS上で**実行**している間、CPUを使用し、メモリ上に何かを保存する特定のプログラム(**プロセス**とも呼ばれる)
|
||||
|
||||
### プロセスとは何か
|
||||
|
||||
**プロセス**という言葉は通常、より具体的な意味で使われ、OSで実行されているものだけを指します(先ほどの最後の説明のように):
|
||||
|
||||
* OS上で**実行**している特定のプログラム
|
||||
* これはファイルやコードを指すのではなく、OSによって**実行**され、管理されているものを指します。
|
||||
* どんなプログラムやコードも、それが**実行されているときにだけ機能**します。つまり、**プロセスとして実行されているときだけ**です。
|
||||
* プロセスは、ユーザーにあるいはOSによって、 **終了**(あるいは "kill")させることができます。その時点で、プロセスは実行/実行されることを停止し、それ以降は**何もできなくなります**。
|
||||
* コンピュータで実行されている各アプリケーションは、実行中のプログラムや各ウィンドウなど、その背後にいくつかのプロセスを持っています。そして通常、コンピュータが起動している間、**多くのプロセスが**同時に実行されています。
|
||||
* **同じプログラム**の**複数のプロセス**が同時に実行されていることがあります。
|
||||
|
||||
OSの「タスク・マネージャー」や「システム・モニター」(または同様のツール)を確認すれば、これらのプロセスの多くが実行されているの見ることができるでしょう。
|
||||
|
||||
例えば、同じブラウザプログラム(Firefox、Chrome、Edgeなど)を実行しているプロセスが複数あることがわかります。通常、1つのタブにつき1つのプロセスが実行され、さらに他のプロセスも実行されます。
|
||||
|
||||
<img class="shadow" src="/img/deployment/concepts/image01.png">
|
||||
|
||||
---
|
||||
|
||||
さて、**プロセス**と**プログラム**という用語の違いを確認したところで、デプロイメントについて話を続けます。
|
||||
|
||||
## 起動時の実行
|
||||
|
||||
ほとんどの場合、Web APIを作成するときは、クライアントがいつでもアクセスできるように、**常に**中断されることなく**実行される**ことを望みます。もちろん、特定の状況でのみ実行させたい特別な理由がある場合は別ですが、その時間のほとんどは、常に実行され、**利用可能**であることを望みます。
|
||||
|
||||
### リモートサーバー上での実行
|
||||
|
||||
リモートサーバー(クラウドサーバー、仮想マシンなど)をセットアップするときにできる最も簡単なことは、ローカルで開発するときと同じように、Uvicorn(または同様のもの)を手動で実行することです。 この方法は**開発中**には役に立つと思われます。
|
||||
|
||||
しかし、サーバーへの接続が切れた場合、**実行中のプロセス**はおそらくダウンしてしまうでしょう。
|
||||
|
||||
そしてサーバーが再起動された場合(アップデートやクラウドプロバイダーからのマイグレーションの後など)、おそらくあなたはそれに**気づかないでしょう**。そのため、プロセスを手動で再起動しなければならないことすら気づかないでしょう。つまり、APIはダウンしたままなのです。😱
|
||||
|
||||
### 起動時に自動的に実行
|
||||
|
||||
一般的に、サーバープログラム(Uvicornなど)はサーバー起動時に自動的に開始され、**人の介入**を必要とせずに、APIと一緒にプロセスが常に実行されるようにしたいと思われます(UvicornがFastAPIアプリを実行するなど)。
|
||||
|
||||
### 別のプログラムの用意
|
||||
|
||||
これを実現するために、通常は**別のプログラム**を用意し、起動時にアプリケーションが実行されるようにします。そして多くの場合、他のコンポーネントやアプリケーション、例えばデータベースも実行されるようにします。
|
||||
|
||||
### 起動時に実行するツールの例
|
||||
|
||||
実行するツールの例をいくつか挙げます:
|
||||
|
||||
* Docker
|
||||
* Kubernetes
|
||||
* Docker Compose
|
||||
* Swarm モードによる Docker
|
||||
* Systemd
|
||||
* Supervisor
|
||||
* クラウドプロバイダーがサービスの一部として内部的に処理
|
||||
* そのほか...
|
||||
|
||||
次の章で、より具体的な例を挙げていきます。
|
||||
|
||||
## 再起動
|
||||
|
||||
起動時にアプリケーションが実行されることを確認するのと同様に、失敗後にアプリケーションが**再起動**されることも確認したいと思われます。
|
||||
|
||||
### 我々は間違いを犯す
|
||||
|
||||
私たち人間は常に**間違い**を犯します。ソフトウェアには、ほとんど常に**バグ**があらゆる箇所に隠されています。🐛
|
||||
|
||||
### 小さなエラーは自動的に処理される
|
||||
|
||||
FastAPIでWeb APIを構築する際に、コードにエラーがある場合、FastAPIは通常、エラーを引き起こした単一のリクエストにエラーを含めます。🛡
|
||||
|
||||
クライアントはそのリクエストに対して**500 Internal Server Error**を受け取りますが、アプリケーションは完全にクラッシュするのではなく、次のリクエストのために動作を続けます。
|
||||
|
||||
### 重大なエラー - クラッシュ
|
||||
|
||||
しかしながら、**アプリケーション全体をクラッシュさせるようなコードを書いて**UvicornとPythonをクラッシュさせるようなケースもあるかもしれません。💥
|
||||
|
||||
それでも、ある箇所でエラーが発生したからといって、アプリケーションを停止させたままにしたくないでしょう。 少なくとも壊れていない*パスオペレーション*については、**実行し続けたい**はずです。
|
||||
|
||||
### クラッシュ後の再起動
|
||||
|
||||
しかし、実行中の**プロセス**をクラッシュさせるような本当にひどいエラーの場合、少なくとも2〜3回ほどプロセスを**再起動**させる外部コンポーネントが必要でしょう。
|
||||
|
||||
/// tip
|
||||
|
||||
...とはいえ、アプリケーション全体が**すぐにクラッシュする**のであれば、いつまでも再起動し続けるのは意味がないでしょう。しかし、その場合はおそらく開発中か少なくともデプロイ直後に気づくと思われます。
|
||||
|
||||
そこで、**将来**クラッシュする可能性があり、それでも再スタートさせることに意味があるような、主なケースに焦点を当ててみます。
|
||||
|
||||
///
|
||||
|
||||
あなたはおそらく**外部コンポーネント**がアプリケーションの再起動を担当することを望むと考えます。 なぜなら、その時点でUvicornとPythonを使った同じアプリケーションはすでにクラッシュしており、同じアプリケーションの同じコードに対して何もできないためです。
|
||||
|
||||
### 自動的に再起動するツールの例
|
||||
|
||||
ほとんどの場合、前述した**起動時にプログラムを実行する**ために使用されるツールは、自動で**再起動**することにも利用されます。
|
||||
|
||||
例えば、次のようなものがあります:
|
||||
|
||||
* Docker
|
||||
* Kubernetes
|
||||
* Docker Compose
|
||||
* Swarm モードによる Docker
|
||||
* Systemd
|
||||
* Supervisor
|
||||
* クラウドプロバイダーがサービスの一部として内部的に処理
|
||||
* そのほか...
|
||||
|
||||
## レプリケーション - プロセスとメモリー
|
||||
|
||||
FastAPI アプリケーションでは、Uvicorn のようなサーバープログラムを使用し、**1つのプロセス**で1度に複数のクライアントに同時に対応できます。
|
||||
|
||||
しかし、多くの場合、複数のワーカー・プロセスを同時に実行したいと考えるでしょう。
|
||||
|
||||
### 複数のプロセス - Worker
|
||||
|
||||
クライアントの数が単一のプロセスで処理できる数を超えており(たとえば仮想マシンがそれほど大きくない場合)、かつサーバーの CPU に**複数のコア**がある場合、同じアプリケーションで同時に**複数のプロセス**を実行させ、すべてのリクエストを分散させることができます。
|
||||
|
||||
同じAPIプログラムの**複数のプロセス**を実行する場合、それらは一般的に**Worker/ワーカー**と呼ばれます。
|
||||
|
||||
### ワーカー・プロセス と ポート
|
||||
<!-- NOTE: https.md written in Japanese does not exist, so it redirects to English one -->
|
||||
|
||||
[HTTPSについて](https.md){.internal-link target=_blank}のドキュメントで、1つのサーバーで1つのポートとIPアドレスの組み合わせでリッスンできるのは1つのプロセスだけであることを覚えていますでしょうか?
|
||||
|
||||
これはいまだに同じです。
|
||||
|
||||
そのため、**複数のプロセス**を同時に持つには**ポートでリッスンしている単一のプロセス**が必要であり、それが何らかの方法で各ワーカー・プロセスに通信を送信することが求められます。
|
||||
|
||||
### プロセスあたりのメモリー
|
||||
|
||||
さて、プログラムがメモリにロードする際には、例えば機械学習モデルや大きなファイルの内容を変数に入れたりする場合では、**サーバーのメモリ(RAM)**を少し消費します。
|
||||
|
||||
そして複数のプロセスは通常、**メモリを共有しません**。これは、実行中の各プロセスがそれぞれ独自の変数やメモリ等を持っていることを意味します。つまり、コード内で大量のメモリを消費している場合、**各プロセス**は同等の量のメモリを消費することになります。
|
||||
|
||||
### サーバーメモリー
|
||||
|
||||
例えば、あなたのコードが **1GBのサイズの機械学習モデル**をロードする場合、APIで1つのプロセスを実行すると、少なくとも1GBのRAMを消費します。
|
||||
|
||||
また、**4つのプロセス**(4つのワーカー)を起動すると、それぞれが1GBのRAMを消費します。つまり、合計でAPIは**4GBのRAM**を消費することになります。
|
||||
|
||||
リモートサーバーや仮想マシンのRAMが3GBしかない場合、4GB以上のRAMをロードしようとすると問題が発生します。🚨
|
||||
|
||||
### 複数プロセス - 例
|
||||
|
||||
この例では、2つの**ワーカー・プロセス**を起動し制御する**マネージャー・ プロセス**があります。
|
||||
|
||||
このマネージャー・ プロセスは、おそらくIPの**ポート**でリッスンしているものです。そして、すべての通信をワーカー・プロセスに転送します。
|
||||
|
||||
これらのワーカー・プロセスは、アプリケーションを実行するものであり、**リクエスト**を受けて**レスポンス**を返すための主要な計算を行い、あなたが変数に入れたものは何でもRAMにロードします。
|
||||
|
||||
<img src="/img/deployment/concepts/process-ram.drawio.svg">
|
||||
|
||||
そしてもちろん、同じマシンでは、あなたのアプリケーションとは別に、**他のプロセス**も実行されているでしょう。
|
||||
|
||||
興味深いことに、各プロセスが使用する**CPU**の割合は時間とともに大きく**変動**する可能性がありますが、**メモリ(RAM)**は通常、多かれ少なかれ**安定**します。
|
||||
|
||||
毎回同程度の計算を行うAPIがあり、多くのクライアントがいるのであれば、**CPU使用率**もおそらく**安定**するでしょう(常に急激に上下するのではなく)。
|
||||
|
||||
### レプリケーション・ツールと戦略の例
|
||||
|
||||
これを実現するにはいくつかのアプローチがありますが、具体的な戦略については次の章(Dockerやコンテナの章など)で詳しく説明します。
|
||||
|
||||
考慮すべき主な制約は、**パブリックIP**の**ポート**を処理する**単一の**コンポーネントが存在しなければならないということです。
|
||||
|
||||
そして、レプリケートされた**プロセス/ワーカー**に通信を**送信**する方法を持つ必要があります。
|
||||
|
||||
考えられる組み合わせと戦略をいくつか紹介します:
|
||||
|
||||
* **Gunicorn**が**Uvicornワーカー**を管理
|
||||
* Gunicornは**IP**と**ポート**をリッスンする**プロセスマネージャ**で、レプリケーションは**複数のUvicornワーカー・プロセス**を持つことによって行われる。
|
||||
* **Uvicorn**が**Uvicornワーカー**を管理
|
||||
* 1つのUvicornの**プロセスマネージャー**が**IP**と**ポート**をリッスンし、**複数のUvicornワーカー・プロセス**を起動する。
|
||||
* **Kubernetes**やその他の分散**コンテナ・システム**
|
||||
* **Kubernetes**レイヤーの何かが**IP**と**ポート**をリッスンする。レプリケーションは、**複数のコンテナ**にそれぞれ**1つのUvicornプロセス**を実行させることで行われる。
|
||||
* **クラウド・サービス**によるレプリケーション
|
||||
* クラウド・サービスはおそらく**あなたのためにレプリケーションを処理**します。**実行するプロセス**や使用する**コンテナイメージ**を定義できるかもしれませんが、いずれにせよ、それはおそらく**単一のUvicornプロセス**であり、クラウドサービスはそのレプリケーションを担当するでしょう。
|
||||
|
||||
/// tip
|
||||
|
||||
これらの**コンテナ**やDockerそしてKubernetesに関する項目が、まだあまり意味をなしていなくても心配しないでください。
|
||||
<!-- NOTE: the current version of docker.md is outdated compared to English one. -->
|
||||
|
||||
コンテナ・イメージ、Docker、Kubernetesなどについては、次の章で詳しく説明します: [コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
## 開始前の事前のステップ
|
||||
|
||||
アプリケーションを**開始する前**に、いくつかのステップを実行したい場合が多くあります。
|
||||
|
||||
例えば、**データベース・マイグレーション** を実行したいかもしれません。
|
||||
|
||||
しかしほとんどの場合、これらの手順を**1度**に実行したいと考えるでしょう。
|
||||
|
||||
そのため、アプリケーションを開始する前の**事前のステップ**を実行する**単一のプロセス**を用意したいと思われます。
|
||||
|
||||
そして、それらの事前のステップを実行しているのが単一のプロセスであることを確認する必要があります。このことはその後アプリケーション自体のために**複数のプロセス**(複数のワーカー)を起動した場合も同様です。
|
||||
|
||||
これらのステップが**複数のプロセス**によって実行された場合、**並列**に実行されることによって作業が**重複**することになります。そして、もしそのステップがデータベースのマイグレーションのような繊細なものであった場合、互いに競合を引き起こす可能性があります。
|
||||
|
||||
もちろん、事前のステップを何度も実行しても問題がない場合もあり、その際は対処がかなり楽になります。
|
||||
|
||||
/// tip
|
||||
|
||||
また、セットアップによっては、アプリケーションを開始する前の**事前のステップ**が必要ない場合もあることを覚えておいてください。
|
||||
|
||||
その場合は、このようなことを心配する必要はないです。🤷
|
||||
|
||||
///
|
||||
|
||||
### 事前ステップの戦略例
|
||||
|
||||
これは**システムを**デプロイする方法に**大きく依存**するだろうし、おそらくプログラムの起動方法や再起動の処理などにも関係してくるでしょう。
|
||||
|
||||
考えられるアイデアをいくつか挙げてみます:
|
||||
|
||||
* アプリコンテナの前に実行されるKubernetesのInitコンテナ
|
||||
* 事前のステップを実行し、アプリケーションを起動するbashスクリプト
|
||||
* 利用するbashスクリプトを起動/再起動したり、エラーを検出したりする方法は以前として必要になるでしょう。
|
||||
|
||||
/// tip
|
||||
|
||||
<!-- NOTE: the current version of docker.md is outdated compared to English one. -->
|
||||
コンテナを使った具体的な例については、次の章で紹介します: [コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
## リソースの利用
|
||||
|
||||
あなたのサーバーは**リソース**であり、プログラムを実行しCPUの計算時間や利用可能なRAMメモリを消費または**利用**することができます。
|
||||
|
||||
システムリソースをどれくらい消費/利用したいですか? 「少ない方が良い」と考えるのは簡単かもしれないですが、実際には、**クラッシュせずに可能な限り**最大限に活用したいでしょう。
|
||||
|
||||
3台のサーバーにお金を払っているにも関わらず、そのRAMとCPUを少ししか使っていないとしたら、おそらく**お金を無駄にしている** 💸、おそらく**サーバーの電力を無駄にしている** 🌎ことになるでしょう。
|
||||
|
||||
その場合は、サーバーを2台だけにして、そのリソース(CPU、メモリ、ディスク、ネットワーク帯域幅など)をより高い割合で使用する方がよいでしょう。
|
||||
|
||||
一方、2台のサーバーがあり、そのCPUとRAMの**100%を使用している**場合、ある時点で1つのプロセスがより多くのメモリを要求し、サーバーはディスクを「メモリ」として使用しないといけません。(何千倍も遅くなる可能性があります。)
|
||||
もしくは**クラッシュ**することもあれば、あるいはあるプロセスが何らかの計算をする必要があり、そしてCPUが再び空くまで待たなければならないかもしれません。
|
||||
|
||||
この場合、**1つ余分なサーバー**を用意し、その上でいくつかのプロセスを実行し、すべてのサーバーが**十分なRAMとCPU時間を持つようにする**のがよいでしょう。
|
||||
|
||||
また、何らかの理由でAPIの利用が急増する可能性もあります。もしかしたらそれが流行ったのかもしれないし、他のサービスやボットが使い始めたのかもしれないです。そのような場合に備えて、余分なリソースを用意しておくと安心でしょう。
|
||||
|
||||
例えば、リソース使用率の**50%から90%の範囲**で**任意の数字**をターゲットとすることができます。
|
||||
|
||||
重要なのは、デプロイメントを微調整するためにターゲットを設定し測定することが、おそらく使用したい主要な要素であることです。
|
||||
|
||||
`htop`のような単純なツールを使って、サーバーで使用されているCPUやRAM、あるいは各プロセスで使用されている量を見ることができます。あるいは、より複雑な監視ツールを使って、サーバに分散して使用することもできます。
|
||||
|
||||
## まとめ
|
||||
|
||||
アプリケーションのデプロイ方法を決定する際に、考慮すべきであろう主要なコンセプトのいくつかを紹介していきました:
|
||||
|
||||
* セキュリティ - HTTPS
|
||||
* 起動時の実行
|
||||
* 再起動
|
||||
* レプリケーション(実行中のプロセス数)
|
||||
* メモリー
|
||||
* 開始前の事前ステップ
|
||||
|
||||
これらの考え方とその適用方法を理解することで、デプロイメントを設定したり調整したりする際に必要な直感的な判断ができるようになるはずです。🤓
|
||||
|
||||
次のセクションでは、あなたが取り得る戦略について、より具体的な例を挙げます。🚀
|
||||
@@ -0,0 +1,748 @@
|
||||
# コンテナ内のFastAPI - Docker
|
||||
|
||||
FastAPIアプリケーションをデプロイする場合、一般的なアプローチは**Linuxコンテナ・イメージ**をビルドすることです。
|
||||
|
||||
基本的には <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>を用いて行われます。生成されたコンテナ・イメージは、いくつかの方法のいずれかでデプロイできます。
|
||||
|
||||
Linuxコンテナの使用には、**セキュリティ**、**反復可能性(レプリカビリティ)**、**シンプリシティ**など、いくつかの利点があります。
|
||||
|
||||
/// tip
|
||||
|
||||
TODO: なぜか遷移できない
|
||||
お急ぎで、すでにこれらの情報をご存じですか? [以下の`Dockerfile`の箇所👇](#build-a-docker-image-for-fastapi)へジャンプしてください。
|
||||
|
||||
///
|
||||
|
||||
<details>
|
||||
<summary>Dockerfile プレビュー 👀</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.9
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
COPY ./app /code/app
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
|
||||
# If running behind a proxy like Nginx or Traefik add --proxy-headers
|
||||
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## コンテナとは何か
|
||||
|
||||
コンテナ(主にLinuxコンテナ)は、同じシステム内の他のコンテナ(他のアプリケーションやコンポーネント)から隔離された状態を保ちながら、すべての依存関係や必要なファイルを含むアプリケーションをパッケージ化する非常に**軽量**な方法です。
|
||||
|
||||
Linuxコンテナは、ホスト(マシン、仮想マシン、クラウドサーバーなど)の同じLinuxカーネルを使用して実行されます。これは、(OS全体をエミュレートする完全な仮想マシンと比べて)非常に軽量であることを意味します。
|
||||
|
||||
このように、コンテナは**リソースをほとんど消費しません**が、プロセスを直接実行するのに匹敵する量です(仮想マシンはもっと消費します)。
|
||||
|
||||
コンテナはまた、独自の**分離された**実行プロセス(通常は1つのプロセスのみ)や、ファイルシステム、ネットワークを持ちます。 このことはデプロイ、セキュリティ、開発などを簡素化させます。
|
||||
|
||||
## コンテナ・イメージとは何か
|
||||
|
||||
**コンテナ**は、**コンテナ・イメージ**から実行されます。
|
||||
|
||||
コンテナ・イメージは、コンテナ内に存在すべきすべてのファイルや環境変数、そしてデフォルトのコマンド/プログラムを**静的に**バージョン化したものです。 ここでの**静的**とは、コンテナ**イメージ**は実行されておらず、パッケージ化されたファイルとメタデータのみであることを意味します。
|
||||
|
||||
保存された静的コンテンツである「**コンテナイメージ**」とは対照的に、「**コンテナ**」は通常、実行中のインスタンス、つまり**実行**されているものを指します。
|
||||
|
||||
**コンテナ**が起動され実行されるとき(**コンテナイメージ**から起動されるとき)、ファイルや環境変数などが作成されたり変更されたりする可能性があります。
|
||||
|
||||
これらの変更はそのコンテナ内にのみ存在しますが、基盤となるコンテナ・イメージには残りません(ディスクに保存されません)。
|
||||
|
||||
コンテナイメージは **プログラム** ファイルやその内容、例えば `python` と `main.py` ファイルに匹敵します。
|
||||
|
||||
そして、**コンテナ**自体は(**コンテナイメージ**とは対照的に)イメージをもとにした実際の実行中のインスタンスであり、**プロセス**に匹敵します。
|
||||
|
||||
実際、コンテナが実行されているのは、**プロセスが実行されている**ときだけです(通常は単一のプロセスだけです)。 コンテナ内で実行中のプロセスがない場合、コンテナは停止します。
|
||||
|
||||
## コンテナ・イメージ
|
||||
|
||||
Dockerは、**コンテナ・イメージ**と**コンテナ**を作成・管理するための主要なツールの1つです。
|
||||
|
||||
そして、DockerにはDockerイメージ(コンテナ)を共有する<a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a>というものがあります。
|
||||
|
||||
Docker Hubは 多くのツールや環境、データベース、アプリケーションに対応している予め作成された**公式のコンテナ・イメージ**をパブリックに提供しています。
|
||||
|
||||
例えば、公式イメージの1つに<a href="https://hub.docker.com/_/python" class="external-link" target="_blank">Python Image</a>があります。
|
||||
|
||||
その他にも、データベースなどさまざまなイメージがあります:
|
||||
|
||||
* <a href="https://hub.docker.com/_/postgres" class="external-link" target="_blank">PostgreSQL</a>
|
||||
* <a href="https://hub.docker.com/_/mysql" class="external-link" target="_blank">MySQL</a>
|
||||
* <a href="https://hub.docker.com/_/mongo" class="external-link" target="_blank">MongoDB</a>
|
||||
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a>, etc.
|
||||
|
||||
予め作成されたコンテナ・イメージを使用することで、異なるツールを**組み合わせて**使用することが非常に簡単になります。例えば、新しいデータベースを試す場合に特に便利です。ほとんどの場合、**公式イメージ**を使い、環境変数で設定するだけで良いです。
|
||||
|
||||
そうすれば多くの場合、コンテナとDockerについて学び、その知識をさまざまなツールやコンポーネントによって再利用することができます。
|
||||
|
||||
つまり、データベース、Pythonアプリケーション、Reactフロントエンド・アプリケーションを備えたウェブ・サーバーなど、さまざまなものを**複数のコンテナ**で実行し、それらを内部ネットワーク経由で接続します。
|
||||
|
||||
すべてのコンテナ管理システム(DockerやKubernetesなど)には、こうしたネットワーキング機能が統合されています。
|
||||
|
||||
## コンテナとプロセス
|
||||
|
||||
通常、**コンテナ・イメージ**はそのメタデータに**コンテナ**の起動時に実行されるデフォルトのプログラムまたはコマンドと、そのプログラムに渡されるパラメータを含みます。コマンドラインでの操作とよく似ています。
|
||||
|
||||
**コンテナ**が起動されると、そのコマンド/プログラムが実行されます(ただし、別のコマンド/プログラムをオーバーライドして実行させることもできます)。
|
||||
|
||||
コンテナは、**メイン・プロセス**(コマンドまたはプログラム)が実行されている限り実行されます。
|
||||
|
||||
コンテナは通常**1つのプロセス**を持ちますが、メイン・プロセスからサブ・プロセスを起動することも可能で、そうすれば同じコンテナ内に**複数のプロセス**を持つことになります。
|
||||
|
||||
しかし、**少なくとも1つの実行中のプロセス**がなければ、実行中のコンテナを持つことはできないです。メイン・プロセスが停止すれば、コンテナも停止します。
|
||||
|
||||
## Build a Docker Image for FastAPI
|
||||
|
||||
ということで、何か作りましょう!🚀
|
||||
|
||||
FastAPI用の**Dockerイメージ**を、**公式Python**イメージに基づいて**ゼロから**ビルドする方法をお見せします。
|
||||
|
||||
これは**ほとんどの場合**にやりたいことです。例えば:
|
||||
|
||||
* **Kubernetes**または同様のツールを使用する場合
|
||||
* **Raspberry Pi**で実行する場合
|
||||
* コンテナ・イメージを実行してくれるクラウド・サービスなどを利用する場合
|
||||
|
||||
### パッケージ要件(package requirements)
|
||||
|
||||
アプリケーションの**パッケージ要件**は通常、何らかのファイルに記述されているはずです。
|
||||
|
||||
パッケージ要件は主に**インストール**するために使用するツールに依存するでしょう。
|
||||
|
||||
最も一般的な方法は、`requirements.txt` ファイルにパッケージ名とそのバージョンを 1 行ずつ書くことです。
|
||||
|
||||
もちろん、[FastAPI バージョンについて](versions.md){.internal-link target=_blank}で読んだのと同じアイデアを使用して、バージョンの範囲を設定します。
|
||||
|
||||
例えば、`requirements.txt` は次のようになります:
|
||||
|
||||
```
|
||||
fastapi>=0.68.0,<0.69.0
|
||||
pydantic>=1.8.0,<2.0.0
|
||||
uvicorn>=0.15.0,<0.16.0
|
||||
```
|
||||
|
||||
そして通常、例えば `pip` を使ってこれらのパッケージの依存関係をインストールします:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install -r requirements.txt
|
||||
---> 100%
|
||||
Successfully installed fastapi pydantic uvicorn
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// info
|
||||
|
||||
パッケージの依存関係を定義しインストールするためのフォーマットやツールは他にもあります。
|
||||
|
||||
Poetryを使った例は、後述するセクションでご紹介します。👇
|
||||
|
||||
///
|
||||
|
||||
### **FastAPI**コードを作成する
|
||||
|
||||
* `app` ディレクトリを作成し、その中に入ります
|
||||
* 空のファイル `__init__.py` を作成します
|
||||
* `main.py` ファイルを作成します:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
### Dockerfile
|
||||
|
||||
同じプロジェクト・ディレクトリに`Dockerfile`というファイルを作成します:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)
|
||||
FROM python:3.9
|
||||
|
||||
# (2)
|
||||
WORKDIR /code
|
||||
|
||||
# (3)
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
# (4)
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (5)
|
||||
COPY ./app /code/app
|
||||
|
||||
# (6)
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
1. 公式のPythonベースイメージから始めます
|
||||
|
||||
2. 現在の作業ディレクトリを `/code` に設定します
|
||||
|
||||
ここに `requirements.txt` ファイルと `app` ディレクトリを置きます。
|
||||
|
||||
3. 要件が書かれたファイルを `/code` ディレクトリにコピーします
|
||||
|
||||
残りのコードではなく、最初に必要なファイルだけをコピーしてください。
|
||||
|
||||
このファイルは**頻繁には変更されない**ので、Dockerはこのステップではそれを検知し**キャッシュ**を使用し、次のステップでもキャッシュを有効にします。
|
||||
|
||||
4. 要件ファイルにあるパッケージの依存関係をインストールします
|
||||
`--no-cache-dir` オプションはダウンロードしたパッケージをローカルに保存しないように `pip` に指示します。これは、同じパッケージをインストールするために `pip` を再度実行する場合にのみ有効ですが、コンテナで作業する場合はそうではないです。
|
||||
|
||||
/// note
|
||||
|
||||
`--no-cache-dir`は`pip`に関連しているだけで、Dockerやコンテナとは何の関係もないです。
|
||||
|
||||
///
|
||||
|
||||
`--upgrade` オプションは、パッケージが既にインストールされている場合、`pip` にアップグレードするように指示します。
|
||||
|
||||
何故ならファイルをコピーする前のステップは**Dockerキャッシュ**によって検出される可能性があるためであり、このステップも利用可能な場合は**Dockerキャッシュ**を使用します。
|
||||
|
||||
このステップでキャッシュを使用すると、開発中にイメージを何度もビルドする際に、**毎回**すべての依存関係を**ダウンロードしてインストールする**代わりに多くの**時間**を**節約**できます。
|
||||
|
||||
5. ./app` ディレクトリを `/code` ディレクトリの中にコピーする。
|
||||
|
||||
これには**最も頻繁に変更される**すべてのコードが含まれているため、Dockerの**キャッシュ**は**これ以降のステップ**に簡単に使用されることはありません。
|
||||
|
||||
そのため、コンテナイメージのビルド時間を最適化するために、`Dockerfile`の **最後** にこれを置くことが重要です。
|
||||
|
||||
6. `uvicorn`サーバーを実行するための**コマンド**を設定します
|
||||
|
||||
`CMD` は文字列のリストを取り、それぞれの文字列はスペースで区切られたコマンドラインに入力するものです。
|
||||
|
||||
このコマンドは **現在の作業ディレクトリ**から実行され、上記の `WORKDIR /code` にて設定した `/code` ディレクトリと同じです。
|
||||
|
||||
そのためプログラムは `/code` で開始しその中にあなたのコードがある `./app` ディレクトリがあるので、**Uvicorn** は `app.main` から `app` を参照し、**インポート** することができます。
|
||||
|
||||
/// tip
|
||||
|
||||
コード内の"+"の吹き出しをクリックして、各行が何をするのかをレビューしてください。👆
|
||||
|
||||
///
|
||||
|
||||
これで、次のようなディレクトリ構造になるはずです:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ └── main.py
|
||||
├── Dockerfile
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
#### TLS Termination Proxyの裏側
|
||||
|
||||
Nginx や Traefik のような TLS Termination Proxy (ロードバランサ) の後ろでコンテナを動かしている場合は、`--proxy-headers`オプションを追加します。
|
||||
|
||||
このオプションは、Uvicornにプロキシ経由でHTTPSで動作しているアプリケーションに対して、送信されるヘッダを信頼するよう指示します。
|
||||
|
||||
```Dockerfile
|
||||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
#### Dockerキャッシュ
|
||||
|
||||
この`Dockerfile`には重要なトリックがあり、まず**依存関係だけのファイル**をコピーします。その理由を説明します。
|
||||
|
||||
```Dockerfile
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
```
|
||||
|
||||
Dockerや他のツールは、これらのコンテナイメージを**段階的に**ビルドし、**1つのレイヤーを他のレイヤーの上に**追加します。`Dockerfile`の先頭から開始し、`Dockerfile`の各命令によって作成されたファイルを追加していきます。
|
||||
|
||||
Dockerや同様のツールは、イメージをビルドする際に**内部キャッシュ**も使用します。前回コンテナイメージを構築したときからファイルが変更されていない場合、ファイルを再度コピーしてゼロから新しいレイヤーを作成する代わりに、**前回作成した同じレイヤーを再利用**します。
|
||||
|
||||
ただファイルのコピーを避けるだけではあまり改善されませんが、そのステップでキャッシュを利用したため、**次のステップ**でキャッシュを使うことができます。
|
||||
|
||||
例えば、依存関係をインストールする命令のためにキャッシュを使うことができます:
|
||||
|
||||
```Dockerfile
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
```
|
||||
|
||||
パッケージ要件のファイルは**頻繁に変更されることはありません**。そのため、そのファイルだけをコピーすることで、Dockerはそのステップでは**キャッシュ**を使用することができます。
|
||||
|
||||
そして、Dockerは**次のステップのためにキャッシュ**を使用し、それらの依存関係をダウンロードしてインストールすることができます。そして、ここで**多くの時間を節約**します。✨ ...そして退屈な待ち時間を避けることができます。😪😆
|
||||
|
||||
パッケージの依存関係をダウンロードしてインストールするには**数分**かかりますが、**キャッシュ**を使えば**せいぜい数秒**です。
|
||||
|
||||
加えて、開発中にコンテナ・イメージを何度もビルドして、コードの変更が機能しているかどうかをチェックすることになるため、多くの時間を節約することができます。
|
||||
|
||||
そして`Dockerfile`の最終行の近くですべてのコードをコピーします。この理由は、**最も頻繁に**変更されるものなので、このステップの後にあるものはほとんどキャッシュを使用することができないのためです。
|
||||
|
||||
```Dockerfile
|
||||
COPY ./app /code/app
|
||||
```
|
||||
|
||||
### Dockerイメージをビルドする
|
||||
|
||||
すべてのファイルが揃ったので、コンテナ・イメージをビルドしましょう。
|
||||
|
||||
* プロジェクトディレクトリに移動します(`Dockerfile`がある場所で、`app`ディレクトリがあります)
|
||||
* FastAPI イメージをビルドします:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ docker build -t myimage .
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// tip
|
||||
|
||||
末尾の `.` に注目してほしいです。これは `./` と同じ意味です。 これはDockerにコンテナイメージのビルドに使用するディレクトリを指示します。
|
||||
|
||||
この場合、同じカレント・ディレクトリ(`.`)です。
|
||||
|
||||
///
|
||||
|
||||
### Dockerコンテナの起動する
|
||||
|
||||
* イメージに基づいてコンテナを実行します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ docker run -d --name mycontainer -p 80:80 myimage
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## 確認する
|
||||
|
||||
Dockerコンテナの<a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> や <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (またはそれに相当するDockerホストを使用したもの)といったURLで確認できるはずです。
|
||||
|
||||
アクセスすると以下のようなものが表示されます:
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
## インタラクティブなAPIドキュメント
|
||||
|
||||
これらのURLにもアクセスできます: <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> や <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (またはそれに相当するDockerホストを使用したもの)
|
||||
|
||||
アクセスすると、自動対話型APIドキュメント(<a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>が提供)が表示されます:
|
||||
|
||||

|
||||
|
||||
## 代替のAPIドキュメント
|
||||
|
||||
また、<a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> や <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (またはそれに相当するDockerホストを使用したもの)にもアクセスできます。
|
||||
|
||||
代替の自動ドキュメント(<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>によって提供される)が表示されます:
|
||||
|
||||

|
||||
|
||||
## 単一ファイルのFastAPIでDockerイメージをビルドする
|
||||
|
||||
FastAPI が単一のファイル、例えば `./app` ディレクトリのない `main.py` の場合、ファイル構造は次のようになります:
|
||||
```
|
||||
.
|
||||
├── Dockerfile
|
||||
├── main.py
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
そうすれば、`Dockerfile`の中にファイルをコピーするために、対応するパスを変更するだけでよいです:
|
||||
|
||||
```{ .dockerfile .annotate hl_lines="10 13" }
|
||||
FROM python:3.9
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (1)
|
||||
COPY ./main.py /code/
|
||||
|
||||
# (2)
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
1. main.py`ファイルを `/code` ディレクトリに直接コピーします。
|
||||
|
||||
2. Uvicornを実行し、`main`から`app`オブジェクトをインポートするように指示します(`app.main`からインポートするのではなく)。
|
||||
|
||||
次にUvicornコマンドを調整して、`app.main` の代わりに新しいモジュール `main` を使用し、FastAPIオブジェクトである `app` をインポートします。
|
||||
|
||||
## デプロイメントのコンセプト
|
||||
|
||||
コンテナという観点から、[デプロイのコンセプト](concepts.md){.internal-link target=_blank}に共通するいくつかについて、もう一度説明しましょう。
|
||||
|
||||
コンテナは主に、アプリケーションの**ビルドとデプロイ**のプロセスを簡素化するためのツールですが、これらの**デプロイのコンセプト**を扱うための特定のアプローチを強制するものではないです。
|
||||
|
||||
**良いニュース**は、それぞれの異なる戦略には、すべてのデプロイメントのコンセプトをカバーする方法があるということです。🎉
|
||||
|
||||
これらの**デプロイメントのコンセプト**をコンテナの観点から見直してみましょう:
|
||||
|
||||
* セキュリティ - HTTPS
|
||||
* 起動時の実行
|
||||
* 再起動
|
||||
* **レプリケーション(実行中のプロセス数)**
|
||||
* メモリ
|
||||
* 開始前の事前ステップ
|
||||
|
||||
## HTTPS
|
||||
|
||||
FastAPI アプリケーションの **コンテナ・イメージ**(および後で実行中の **コンテナ**)だけに焦点を当てると、通常、HTTPSは別のツールを用いて**外部で**処理されます。
|
||||
|
||||
例えば<a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>のように、**HTTPS**と**証明書**の**自動**取得を扱う別のコンテナである可能性もあります。
|
||||
|
||||
/// tip
|
||||
|
||||
TraefikはDockerやKubernetesなどと統合されているので、コンテナ用のHTTPSの設定や構成はとても簡単です。
|
||||
|
||||
///
|
||||
|
||||
あるいは、(コンテナ内でアプリケーションを実行しながら)クラウド・プロバイダーがサービスの1つとしてHTTPSを処理することもできます。
|
||||
|
||||
## 起動時および再起動時の実行
|
||||
|
||||
通常、コンテナの**起動と実行**を担当する別のツールがあります。
|
||||
|
||||
それは直接**Docker**であったり、**Docker Compose**であったり、**Kubernetes**であったり、**クラウドサービス**であったりします。
|
||||
|
||||
ほとんどの場合(またはすべての場合)、起動時にコンテナを実行し、失敗時に再起動を有効にする簡単なオプションがあります。例えばDockerでは、コマンドラインオプションの`--restart`が該当します。
|
||||
|
||||
コンテナを使わなければ、アプリケーションを起動時や再起動時に実行させるのは面倒で難しいかもしれません。しかし、**コンテナ**で作業する場合、ほとんどのケースでその機能はデフォルトで含まれています。✨
|
||||
|
||||
## レプリケーション - プロセス数
|
||||
|
||||
**Kubernetes** や Docker Swarm モード、Nomad、あるいは複数のマシン上で分散コンテナを管理するための同様の複雑なシステムを使ってマシンの<abbr title="何らかの方法で接続され、一緒に動作するように構成されたマシンのグループ">クラスター</abbr>を構成している場合、 各コンテナで(Workerを持つGunicornのような)**プロセスマネージャ**を使用する代わりに、**クラスター・レベル**で**レプリケーション**を処理したいと思うでしょう。
|
||||
|
||||
Kubernetesのような分散コンテナ管理システムの1つは通常、入ってくるリクエストの**ロードバランシング**をサポートしながら、**コンテナのレプリケーション**を処理する統合された方法を持っています。このことはすべて**クラスタレベル**にてです。
|
||||
|
||||
そのような場合、UvicornワーカーでGunicornのようなものを実行するのではなく、[上記の説明](#dockerfile)のように**Dockerイメージをゼロから**ビルドし、依存関係をインストールして、**単一のUvicornプロセス**を実行したいでしょう。
|
||||
|
||||
### ロードバランサー
|
||||
|
||||
コンテナを使用する場合、通常はメイン・ポート**でリスニング**しているコンポーネントがあるはずです。それはおそらく、**HTTPS**を処理するための**TLS Termination Proxy**でもある別のコンテナであったり、同様のツールであったりするでしょう。
|
||||
|
||||
このコンポーネントはリクエストの **負荷** を受け、 (うまくいけば) その負荷を**バランスよく** ワーカーに分配するので、一般に **ロードバランサ** とも呼ばれます。
|
||||
|
||||
/// tip
|
||||
|
||||
HTTPSに使われるものと同じ**TLS Termination Proxy**コンポーネントは、おそらく**ロードバランサー**にもなるでしょう。
|
||||
|
||||
///
|
||||
|
||||
そしてコンテナで作業する場合、コンテナの起動と管理に使用する同じシステムには、**ロードバランサー**(**TLS Termination Proxy**の可能性もある)から**ネットワーク通信**(HTTPリクエストなど)をアプリのあるコンテナ(複数可)に送信するための内部ツールが既にあるはずです。
|
||||
|
||||
### 1つのロードバランサー - 複数のワーカーコンテナー
|
||||
|
||||
**Kubernetes**や同様の分散コンテナ管理システムで作業する場合、その内部のネットワーキングのメカニズムを使用することで、メインの**ポート**でリッスンしている単一の**ロードバランサー**が、アプリを実行している可能性のある**複数のコンテナ**に通信(リクエスト)を送信できるようになります。
|
||||
|
||||
アプリを実行するこれらのコンテナには、通常**1つのプロセス**(たとえば、FastAPIアプリケーションを実行するUvicornプロセス)があります。これらはすべて**同一のコンテナ**であり同じものを実行しますが、それぞれが独自のプロセスやメモリなどを持ちます。そうすることで、CPUの**異なるコア**、あるいは**異なるマシン**での**並列化**を利用できます。
|
||||
|
||||
そして、**ロードバランサー**を備えた分散コンテナシステムは、**順番に**あなたのアプリを含む各コンテナに**リクエストを分配**します。つまり、各リクエストは、あなたのアプリを実行している複数の**レプリケートされたコンテナ**の1つによって処理されます。
|
||||
|
||||
そして通常、この**ロードバランサー**は、クラスタ内の*他の*アプリケーション(例えば、異なるドメインや異なるURLパスのプレフィックスの配下)へのリクエストを処理することができ、その通信をクラスタ内で実行されている*他の*アプリケーションのための適切なコンテナに送信します。
|
||||
|
||||
### 1コンテナにつき1プロセス
|
||||
|
||||
この種のシナリオでは、すでにクラスタ・レベルでレプリケーションを処理しているため、おそらくコンテナごとに**単一の(Uvicorn)プロセス**を持ちたいでしょう。
|
||||
|
||||
この場合、Uvicornワーカーを持つGunicornのようなプロセスマネージャーや、Uvicornワーカーを使うUvicornは**避けたい**でしょう。**コンテナごとにUvicornのプロセスは1つだけ**にしたいでしょう(おそらく複数のコンテナが必要でしょう)。
|
||||
|
||||
(GunicornやUvicornがUvicornワーカーを管理するように)コンテナ内に別のプロセスマネージャーを持つことは、クラスターシステムですでに対処しているであろう**不要な複雑さ**を追加するだけです。
|
||||
|
||||
### Containers with Multiple Processes and Special Cases
|
||||
|
||||
もちろん、**特殊なケース**として、**Gunicornプロセスマネージャ**を持つ**コンテナ**内で複数の**Uvicornワーカープロセス**を起動させたい場合があります。
|
||||
|
||||
このような場合、**公式のDockerイメージ**を使用することができます。このイメージには、複数の**Uvicornワーカープロセス**を実行するプロセスマネージャとして**Gunicorn**が含まれており、現在のCPUコアに基づいてワーカーの数を自動的に調整するためのデフォルト設定がいくつか含まれています。詳しくは後述の[Gunicornによる公式Dockerイメージ - Uvicorn](#gunicorndocker-uvicorn)で説明します。
|
||||
|
||||
以下は、それが理にかなっている場合の例です:
|
||||
|
||||
#### シンプルなアプリケーション
|
||||
|
||||
アプリケーションを**シンプル**な形で実行する場合、プロセス数の細かい調整が必要ない場合、自動化されたデフォルトを使用するだけで、コンテナ内にプロセスマネージャが必要かもしれません。例えば、公式Dockerイメージでシンプルな設定が可能です。
|
||||
|
||||
#### Docker Compose
|
||||
|
||||
Docker Composeで**シングルサーバ**(クラスタではない)にデプロイすることもできますので、共有ネットワークと**ロードバランシング**を維持しながら(Docker Composeで)コンテナのレプリケーションを管理する簡単な方法はないでしょう。
|
||||
|
||||
その場合、**単一のコンテナ**で、**プロセスマネージャ**が内部で**複数のワーカープロセス**を起動するようにします。
|
||||
|
||||
#### Prometheusとその他の理由
|
||||
|
||||
また、**1つのコンテナ**に**1つのプロセス**を持たせるのではなく、**1つのコンテナ**に**複数のプロセス**を持たせる方が簡単だという**他の理由**もあるでしょう。
|
||||
|
||||
例えば、(セットアップにもよりますが)Prometheusエクスポーターのようなツールを同じコンテナ内に持つことができます。
|
||||
|
||||
この場合、**複数のコンテナ**があると、デフォルトでは、Prometheusが**メトリクスを**読みに来たとき、すべてのレプリケートされたコンテナの**蓄積されたメトリクス**を取得するのではなく、毎回**単一のコンテナ**(その特定のリクエストを処理したコンテナ)のものを取得することになります。
|
||||
|
||||
その場合、**複数のプロセス**を持つ**1つのコンテナ**を用意し、同じコンテナ上のローカルツール(例えばPrometheusエクスポーター)がすべての内部プロセスのPrometheusメトリクスを収集し、その1つのコンテナ上でそれらのメトリクスを公開する方がシンプルかもしれません。
|
||||
|
||||
---
|
||||
|
||||
重要なのは、盲目的に従わなければならない普遍のルールはないということです。
|
||||
|
||||
これらのアイデアは、**あなた自身のユースケース**を評価し、あなたのシステムに最適なアプローチを決定するために使用することができます:
|
||||
|
||||
* セキュリティ - HTTPS
|
||||
* 起動時の実行
|
||||
* 再起動
|
||||
* **レプリケーション(実行中のプロセス数)**
|
||||
* メモリ
|
||||
* 開始前の事前ステップ
|
||||
|
||||
## メモリー
|
||||
|
||||
コンテナごとに**単一のプロセスを実行する**と、それらのコンテナ(レプリケートされている場合は1つ以上)によって消費される多かれ少なかれ明確に定義された、安定し制限された量のメモリを持つことになります。
|
||||
|
||||
そして、コンテナ管理システム(**Kubernetes**など)の設定で、同じメモリ制限と要件を設定することができます。
|
||||
|
||||
そうすれば、コンテナが必要とするメモリ量とクラスタ内のマシンで利用可能なメモリ量を考慮して、**利用可能なマシン**に**コンテナ**をレプリケートできるようになります。
|
||||
|
||||
アプリケーションが**シンプル**なものであれば、これはおそらく**問題にはならない**でしょうし、ハードなメモリ制限を指定する必要はないかもしれないです。
|
||||
|
||||
しかし、**多くのメモリを使用**している場合(たとえば**機械学習**モデルなど)、どれだけのメモリを消費しているかを確認し、**各マシンで実行するコンテナの数**を調整する必要があります(そしておそらくクラスタにマシンを追加します)。
|
||||
|
||||
**コンテナごとに複数のプロセス**を実行する場合(たとえば公式のDockerイメージで)、起動するプロセスの数が**利用可能なメモリ以上に消費しない**ようにする必要があります。
|
||||
|
||||
## 開始前の事前ステップとコンテナ
|
||||
|
||||
コンテナ(DockerやKubernetesなど)を使っている場合、主に2つのアプローチがあります。
|
||||
|
||||
### 複数のコンテナ
|
||||
|
||||
複数の**コンテナ**があり、おそらくそれぞれが**単一のプロセス**を実行している場合(**Kubernetes**クラスタなど)、レプリケートされたワーカーコンテナを実行する**前に**、単一のコンテナで**事前のステップ**の作業を行う**別のコンテナ**を持ちたいと思うでしょう。
|
||||
|
||||
/// info
|
||||
|
||||
もしKubernetesを使用している場合, これはおそらく<a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init コンテナ</a>でしょう。
|
||||
|
||||
///
|
||||
|
||||
ユースケースが事前のステップを**並列で複数回**実行するのに問題がない場合(例:データベースの準備チェック)、メインプロセスを開始する前に、それらのステップを各コンテナに入れることが可能です。
|
||||
|
||||
### 単一コンテナ
|
||||
|
||||
単純なセットアップで、**単一のコンテナ**で複数の**ワーカー・プロセス**(または1つのプロセスのみ)を起動する場合、アプリでプロセスを開始する直前に、同じコンテナで事前のステップを実行できます。公式Dockerイメージは、内部的にこれをサポートしています。
|
||||
|
||||
## Gunicornによる公式Dockerイメージ - Uvicorn
|
||||
|
||||
前の章で詳しく説明したように、Uvicornワーカーで動作するGunicornを含む公式のDockerイメージがあります: [Server Workers - Gunicorn と Uvicorn](server-workers.md){.internal-link target=_blank}で詳しく説明しています。
|
||||
|
||||
このイメージは、主に上記で説明した状況で役に立つでしょう: [複数のプロセスと特殊なケースを持つコンテナ(Containers with Multiple Processes and Special Cases)](#containers-with-multiple-processes-and-special-cases)
|
||||
|
||||
* <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
|
||||
/// warning
|
||||
|
||||
このベースイメージや類似のイメージは**必要ない**可能性が高いので、[上記の: FastAPI用のDockerイメージをビルドする(Build a Docker Image for FastAPI)](#build-a-docker-image-for-fastapi)のようにゼロからイメージをビルドする方が良いでしょう。
|
||||
|
||||
///
|
||||
|
||||
このイメージには、利用可能なCPUコアに基づいて**ワーカー・プロセスの数**を設定する**オートチューニング**メカニズムが含まれています。
|
||||
|
||||
これは**賢明なデフォルト**を備えていますが、**環境変数**や設定ファイルを使ってすべての設定を変更したり更新したりすることができます。
|
||||
|
||||
また、スクリプトで<a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#pre_start_path" class="external-link" target="_blank">**開始前の事前ステップ**</a>を実行することもサポートしている。
|
||||
|
||||
/// tip
|
||||
|
||||
すべての設定とオプションを見るには、Dockerイメージのページをご覧ください: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>
|
||||
|
||||
///
|
||||
|
||||
### 公式Dockerイメージのプロセス数
|
||||
|
||||
このイメージの**プロセス数**は、利用可能なCPU**コア**から**自動的に計算**されます。
|
||||
|
||||
つまり、CPUから可能な限り**パフォーマンス**を**引き出そう**とします。
|
||||
|
||||
また、**環境変数**などを使った設定で調整することもできます。
|
||||
|
||||
しかし、プロセスの数はコンテナが実行しているCPUに依存するため、**消費されるメモリの量**もそれに依存することになります。
|
||||
|
||||
そのため、(機械学習モデルなどで)大量のメモリを消費するアプリケーションで、サーバーのCPUコアが多いが**メモリが少ない**場合、コンテナは利用可能なメモリよりも多くのメモリを使おうとすることになります。
|
||||
|
||||
その結果、パフォーマンスが大幅に低下する(あるいはクラッシュする)可能性があります。🚨
|
||||
|
||||
### Dockerfileを作成する
|
||||
|
||||
この画像に基づいて`Dockerfile`を作成する方法を以下に示します:
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
|
||||
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
|
||||
COPY ./app /app
|
||||
```
|
||||
|
||||
### より大きなアプリケーション
|
||||
|
||||
[複数のファイルを持つ大きなアプリケーション](../tutorial/bigger-applications.md){.internal-link target=_blank}を作成するセクションに従った場合、`Dockerfile`は次のようになります:
|
||||
|
||||
```Dockerfile hl_lines="7"
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
|
||||
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
|
||||
COPY ./app /app/app
|
||||
```
|
||||
|
||||
### いつ使うのか
|
||||
|
||||
おそらく、**Kubernetes**(または他のもの)を使用していて、すでにクラスタレベルで複数の**コンテナ**で**レプリケーション**を設定している場合は、この公式ベースイメージ(または他の類似のもの)は**使用すべきではありません**。
|
||||
|
||||
そのような場合は、上記のように**ゼロから**イメージを構築する方がよいでしょう: [FastAPI用のDockerイメージをビルドする(Build a Docker Image for FastAPI)](#build-a-docker-image-for-fastapi) を参照してください。
|
||||
|
||||
このイメージは、主に上記の[複数のプロセスと特殊なケースを持つコンテナ(Containers with Multiple Processes and Special Cases)](#containers-with-multiple-processes-and-special-cases)で説明したような特殊なケースで役に立ちます。
|
||||
|
||||
例えば、アプリケーションが**シンプル**で、CPUに応じたデフォルトのプロセス数を設定すればうまくいく場合や、クラスタレベルでレプリケーションを手動で設定する手間を省きたい場合、アプリで複数のコンテナを実行しない場合などです。
|
||||
|
||||
または、**Docker Compose**でデプロイし、単一のサーバで実行している場合などです。
|
||||
|
||||
## コンテナ・イメージのデプロイ
|
||||
|
||||
コンテナ(Docker)イメージを手に入れた後、それをデプロイするにはいくつかの方法があります。
|
||||
|
||||
例えば以下のリストの方法です:
|
||||
|
||||
* 単一サーバーの**Docker Compose**
|
||||
* **Kubernetes**クラスタ
|
||||
* Docker Swarmモードのクラスター
|
||||
* Nomadのような別のツール
|
||||
* コンテナ・イメージをデプロイするクラウド・サービス
|
||||
|
||||
## Poetryを利用したDockerイメージ
|
||||
|
||||
もしプロジェクトの依存関係を管理するために<a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a>を利用する場合、マルチステージビルドを使うと良いでしょう。
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)
|
||||
FROM python:3.9 as requirements-stage
|
||||
|
||||
# (2)
|
||||
WORKDIR /tmp
|
||||
|
||||
# (3)
|
||||
RUN pip install poetry
|
||||
|
||||
# (4)
|
||||
COPY ./pyproject.toml ./poetry.lock* /tmp/
|
||||
|
||||
# (5)
|
||||
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
|
||||
|
||||
# (6)
|
||||
FROM python:3.9
|
||||
|
||||
# (7)
|
||||
WORKDIR /code
|
||||
|
||||
# (8)
|
||||
COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt
|
||||
|
||||
# (9)
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (10)
|
||||
COPY ./app /code/app
|
||||
|
||||
# (11)
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
1. これは最初のステージで、`requirements-stage`と名付けられます
|
||||
2. `/tmp` を現在の作業ディレクトリに設定します
|
||||
ここで `requirements.txt` というファイルを生成します。
|
||||
|
||||
3. このDockerステージにPoetryをインストールします
|
||||
|
||||
4. pyproject.toml`と`poetry.lock`ファイルを`/tmp` ディレクトリにコピーします
|
||||
|
||||
`./poetry.lock*`(末尾に`*`)を使用するため、そのファイルがまだ利用できない場合でもクラッシュすることはないです。
|
||||
5. requirements.txt`ファイルを生成します
|
||||
|
||||
6. これは最後のステージであり、ここにあるものはすべて最終的なコンテナ・イメージに保存されます
|
||||
7. 現在の作業ディレクトリを `/code` に設定します
|
||||
8. `requirements.txt`ファイルを `/code` ディレクトリにコピーします
|
||||
このファイルは前のDockerステージにしか存在しないため、`--from-requirements-stage`を使ってコピーします。
|
||||
9. 生成された `requirements.txt` ファイルにあるパッケージの依存関係をインストールします
|
||||
10. app` ディレクトリを `/code` ディレクトリにコピーします
|
||||
11. uvicorn` コマンドを実行して、`app.main` からインポートした `app` オブジェクトを使用するように指示します
|
||||
/// tip
|
||||
|
||||
"+"の吹き出しをクリックすると、それぞれの行が何をするのかを見ることができます
|
||||
|
||||
///
|
||||
|
||||
**Dockerステージ**は`Dockerfile`の一部で、**一時的なコンテナイメージ**として動作します。
|
||||
|
||||
最初のステージは **Poetryのインストール**と Poetry の `pyproject.toml` ファイルからプロジェクトの依存関係を含む**`requirements.txt`を生成**するためだけに使用されます。
|
||||
|
||||
この `requirements.txt` ファイルは後半の **次のステージ**で `pip` と共に使用されます。
|
||||
|
||||
最終的なコンテナイメージでは、**最終ステージ**のみが保存されます。前のステージは破棄されます。
|
||||
|
||||
Poetryを使用する場合、**Dockerマルチステージビルド**を使用することは理にかなっています。
|
||||
|
||||
なぜなら、最終的なコンテナイメージにPoetryとその依存関係がインストールされている必要はなく、**必要なのは**プロジェクトの依存関係をインストールするために生成された `requirements.txt` ファイルだけだからです。
|
||||
|
||||
そして次の(そして最終的な)ステージでは、前述とほぼ同じ方法でイメージをビルドします。
|
||||
|
||||
### TLS Termination Proxyの裏側 - Poetry
|
||||
|
||||
繰り返しになりますが、NginxやTraefikのようなTLS Termination Proxy(ロードバランサー)の後ろでコンテナを動かしている場合は、`--proxy-headers`オプションをコマンドに追加します:
|
||||
|
||||
```Dockerfile
|
||||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
|
||||
```
|
||||
|
||||
## まとめ
|
||||
|
||||
コンテナ・システム(例えば**Docker**や**Kubernetes**など)を使えば、すべての**デプロイメントのコンセプト**を扱うのがかなり簡単になります:
|
||||
|
||||
* セキュリティ - HTTPS
|
||||
* 起動時の実行
|
||||
* 再起動
|
||||
* **レプリケーション(実行中のプロセス数)**
|
||||
* メモリ
|
||||
* 開始前の事前ステップ
|
||||
|
||||
ほとんどの場合、ベースとなるイメージは使用せず、公式のPython Dockerイメージをベースにした**コンテナイメージをゼロからビルド**します。
|
||||
|
||||
`Dockerfile`と**Dockerキャッシュ**内の命令の**順番**に注意することで、**ビルド時間を最小化**することができ、生産性を最大化することができます(そして退屈を避けることができます)。😎
|
||||
|
||||
特別なケースでは、FastAPI用の公式Dockerイメージを使いたいかもしれません。🤓
|
||||
@@ -0,0 +1,209 @@
|
||||
# HTTPS について
|
||||
|
||||
HTTPSは単に「有効」か「無効」かで決まるものだと思いがちです。
|
||||
|
||||
しかし、それよりもはるかに複雑です。
|
||||
|
||||
/// tip
|
||||
|
||||
もし急いでいたり、HTTPSの仕組みについて気にしないのであれば、次のセクションに進み、さまざまなテクニックを使ってすべてをセットアップするステップ・バイ・ステップの手順をご覧ください。
|
||||
|
||||
///
|
||||
|
||||
利用者の視点から **HTTPS の基本を学ぶ**に当たっては、次のリソースをオススメします: <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
|
||||
|
||||
さて、**開発者の視点**から、HTTPSについて考える際に念頭に置くべきことをいくつかみていきましょう:
|
||||
|
||||
* HTTPSの場合、**サーバ**は**第三者**によって生成された**「証明書」を持つ**必要があります。
|
||||
* これらの証明書は「生成」されたものではなく、実際には第三者から**取得**されたものです。
|
||||
* 証明書には**有効期限**があります。
|
||||
* つまりいずれ失効します。
|
||||
* そのため**更新**をし、第三者から**再度取得**する必要があります。
|
||||
* 接続の暗号化は**TCPレベル**で行われます。
|
||||
* それは**HTTPの1つ下**のレイヤーです。
|
||||
* つまり、**証明書と暗号化**の処理は、**HTTPの前**に行われます。
|
||||
* **TCPは "ドメイン "について知りません**。IPアドレスについてのみ知っています。
|
||||
* 要求された**特定のドメイン**に関する情報は、**HTTPデータ**に入ります。
|
||||
* **HTTPS証明書**は、**特定のドメイン**を「証明」しますが、プロトコルと暗号化はTCPレベルで行われ、どのドメインが扱われているかを**知る前**に行われます。
|
||||
* **デフォルトでは**、**IPアドレスごとに1つのHTTPS証明書**しか持てないことになります。
|
||||
* これは、サーバーの規模やアプリケーションの規模に寄りません。
|
||||
* しかし、これには**解決策**があります。
|
||||
* **TLS**プロトコル(HTTPの前に、TCPレベルで暗号化を処理するもの)には、**<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="サーバー名表示">SNI</abbr></a>**と呼ばれる**拡張**があります。
|
||||
* このSNI拡張機能により、1つのサーバー(**単一のIPアドレス**を持つ)が**複数のHTTPS証明書**を持ち、**複数のHTTPSドメイン/アプリケーション**にサービスを提供できるようになります。
|
||||
* これが機能するためには、**パブリックIPアドレス**でリッスンしている、サーバー上で動作している**単一の**コンポーネント(プログラム)が、サーバー内の**すべてのHTTPS証明書**を持っている必要があります。
|
||||
|
||||
* セキュアな接続を取得した**後**でも、通信プロトコルは**HTTPのまま**です。
|
||||
* コンテンツは**HTTPプロトコル**で送信されているにもかかわらず、**暗号化**されています。
|
||||
|
||||
|
||||
サーバー(マシン、ホストなど)上で**1つのプログラム/HTTPサーバー**を実行させ、**HTTPSに関する全てのこと**を管理するのが一般的です。
|
||||
|
||||
**暗号化された HTTPS リクエスト** を受信し、**復号化された HTTP リクエスト** を同じサーバーで実行されている実際の HTTP アプリケーション(この場合は **FastAPI** アプリケーション)に送信し、アプリケーションから **HTTP レスポンス** を受け取り、適切な **HTTPS 証明書** を使用して **暗号化** し、そして**HTTPS** を使用してクライアントに送り返します。
|
||||
|
||||
このサーバーはしばしば **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS Termination Proxy</a>**と呼ばれます。
|
||||
|
||||
TLS Termination Proxyとして使えるオプションには、以下のようなものがあります:
|
||||
|
||||
* Traefik(証明書の更新も対応)
|
||||
* Caddy (証明書の更新も対応)
|
||||
* Nginx
|
||||
* HAProxy
|
||||
|
||||
|
||||
## Let's Encrypt
|
||||
|
||||
Let's Encrypt以前は、これらの**HTTPS証明書**は信頼できる第三者によって販売されていました。
|
||||
|
||||
これらの証明書を取得するための手続きは面倒で、かなりの書類を必要とし、証明書はかなり高価なものでした。
|
||||
|
||||
しかしその後、**<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>** が作られました。
|
||||
|
||||
これはLinux Foundationのプロジェクトから生まれたものです。 自動化された方法で、**HTTPS証明書を無料で**提供します。これらの証明書は、すべての標準的な暗号化セキュリティを使用し、また短命(約3ヶ月)ですが、こういった寿命の短さによって、**セキュリティは実際に優れています**。
|
||||
|
||||
ドメインは安全に検証され、証明書は自動的に生成されます。また、証明書の更新も自動化されます。
|
||||
|
||||
このアイデアは、これらの証明書の取得と更新を自動化することで、**安全なHTTPSを、無料で、永遠に**利用できるようにすることです。
|
||||
|
||||
## 開発者のための HTTPS
|
||||
|
||||
ここでは、HTTPS APIがどのように見えるかの例を、主に開発者にとって重要なアイデアに注意を払いながら、ステップ・バイ・ステップで説明します。
|
||||
|
||||
### ドメイン名
|
||||
|
||||
ステップの初めは、**ドメイン名**を**取得すること**から始まるでしょう。その後、DNSサーバー(おそらく同じクラウドプロバイダー)に設定します。
|
||||
|
||||
おそらくクラウドサーバー(仮想マシン)かそれに類するものを手に入れ、<abbr title="変わらない">固定の</abbr> **パブリックIPアドレス**を持つことになるでしょう。
|
||||
|
||||
DNSサーバーでは、**取得したドメイン**をあなたのサーバーのパプリック**IPアドレス**に向けるレコード(「`Aレコード`」)を設定します。
|
||||
|
||||
これはおそらく、最初の1回だけあり、すべてをセットアップするときに行うでしょう。
|
||||
|
||||
/// tip
|
||||
|
||||
ドメイン名の話はHTTPSに関する話のはるか前にありますが、すべてがドメインとIPアドレスに依存するため、ここで言及する価値があります。
|
||||
|
||||
///
|
||||
|
||||
### DNS
|
||||
|
||||
では、実際のHTTPSの部分に注目してみよう。
|
||||
|
||||
まず、ブラウザは**DNSサーバー**に**ドメインに対するIP**が何であるかを確認します。今回は、`someapp.example.com`とします。
|
||||
|
||||
DNSサーバーは、ブラウザに特定の**IPアドレス**を使用するように指示します。このIPアドレスは、DNSサーバーで設定した、あなたのサーバーが使用するパブリックIPアドレスになります。
|
||||
|
||||
<img src="/img/deployment/https/https01.drawio.svg">
|
||||
|
||||
### TLS Handshake の開始
|
||||
|
||||
ブラウザはIPアドレスと**ポート443**(HTTPSポート)で通信します。
|
||||
|
||||
通信の最初の部分は、クライアントとサーバー間の接続を確立し、使用する暗号鍵などを決めるだけです。
|
||||
|
||||
<img src="/img/deployment/https/https02.drawio.svg">
|
||||
|
||||
TLS接続を確立するためのクライアントとサーバー間のこのやりとりは、**TLSハンドシェイク**と呼ばれます。
|
||||
|
||||
### SNI拡張機能付きのTLS
|
||||
|
||||
サーバー内の**1つのプロセス**だけが、特定 の**IPアドレス**の特定の**ポート** で待ち受けることができます。
|
||||
|
||||
同じIPアドレスの他のポートで他のプロセスがリッスンしている可能性もありますが、IPアドレスとポートの組み合わせごとに1つだけです。
|
||||
|
||||
TLS(HTTPS)はデフォルトで`443`という特定のポートを使用する。つまり、これが必要なポートです。
|
||||
|
||||
このポートをリッスンできるのは1つのプロセスだけなので、これを実行するプロセスは**TLS Termination Proxy**となります。
|
||||
|
||||
TLS Termination Proxyは、1つ以上の**TLS証明書**(HTTPS証明書)にアクセスできます。
|
||||
|
||||
前述した**SNI拡張機能**を使用して、TLS Termination Proxy は、利用可能なTLS (HTTPS)証明書のどれを接続先として使用すべきかをチェックし、クライアントが期待するドメインに一致するものを使用します。
|
||||
|
||||
今回は、`someapp.example.com`の証明書を使うことになります。
|
||||
|
||||
<img src="/img/deployment/https/https03.drawio.svg">
|
||||
|
||||
クライアントは、そのTLS証明書を生成したエンティティ(この場合はLet's Encryptですが、これについては後述します)をすでに**信頼**しているため、その証明書が有効であることを**検証**することができます。
|
||||
|
||||
次に証明書を使用して、クライアントとTLS Termination Proxy は、 **TCP通信**の残りを**どのように暗号化するかを決定**します。これで**TLSハンドシェイク**の部分が完了します。
|
||||
|
||||
この後、クライアントとサーバーは**暗号化されたTCP接続**を持ちます。そして、その接続を使って実際の**HTTP通信**を開始することができます。
|
||||
|
||||
これが**HTTPS**であり、純粋な(暗号化されていない)TCP接続ではなく、**セキュアなTLS接続**の中に**HTTP**があるだけです。
|
||||
|
||||
/// tip
|
||||
|
||||
通信の暗号化は、HTTPレベルではなく、**TCPレベル**で行われることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
### HTTPS リクエスト
|
||||
|
||||
これでクライアントとサーバー(具体的にはブラウザとTLS Termination Proxy)は**暗号化されたTCP接続**を持つことになり、**HTTP通信**を開始することができます。
|
||||
|
||||
そこで、クライアントは**HTTPSリクエスト**を送信します。これは、暗号化されたTLSコネクションを介した単なるHTTPリクエストです。
|
||||
|
||||
<img src="/img/deployment/https/https04.drawio.svg">
|
||||
|
||||
### リクエストの復号化
|
||||
|
||||
TLS Termination Proxy は、合意が取れている暗号化を使用して、**リクエストを復号化**し、**プレーン (復号化された) HTTP リクエスト** をアプリケーションを実行しているプロセス (例えば、FastAPI アプリケーションを実行している Uvicorn を持つプロセス) に送信します。
|
||||
|
||||
<img src="/img/deployment/https/https05.drawio.svg">
|
||||
|
||||
### HTTP レスポンス
|
||||
|
||||
アプリケーションはリクエストを処理し、**プレーン(暗号化されていない)HTTPレスポンス** をTLS Termination Proxyに送信します。
|
||||
|
||||
<img src="/img/deployment/https/https06.drawio.svg">
|
||||
|
||||
### HTTPS レスポンス
|
||||
|
||||
TLS Termination Proxyは次に、事前に合意が取れている暗号(`someapp.example.com`の証明書から始まる)を使って**レスポンスを暗号化し**、ブラウザに送り返す。
|
||||
|
||||
その後ブラウザでは、レスポンスが有効で正しい暗号キーで暗号化されていることなどを検証します。そして、ブラウザはレスポンスを**復号化**して処理します。
|
||||
|
||||
<img src="/img/deployment/https/https07.drawio.svg">
|
||||
|
||||
クライアント(ブラウザ)は、レスポンスが正しいサーバーから来たことを知ることができます。 なぜなら、そのサーバーは、以前に**HTTPS証明書**を使って合意した暗号を使っているからです。
|
||||
|
||||
### 複数のアプリケーション
|
||||
|
||||
同じサーバー(または複数のサーバー)に、例えば他のAPIプログラムやデータベースなど、**複数のアプリケーション**が存在する可能性があります。
|
||||
|
||||
特定のIPとポート(この例ではTLS Termination Proxy)を扱うことができるのは1つのプロセスだけですが、他のアプリケーション/プロセスも、同じ**パブリックIPとポート**の組み合わせを使用しようとしない限り、サーバー上で実行することができます。
|
||||
|
||||
<img src="/img/deployment/https/https08.drawio.svg">
|
||||
|
||||
そうすれば、TLS Termination Proxy は、**複数のドメイン**や複数のアプリケーションのHTTPSと証明書を処理し、それぞれのケースで適切なアプリケーションにリクエストを送信することができます。
|
||||
|
||||
### 証明書の更新
|
||||
|
||||
将来のある時点で、各証明書は(取得後約3ヶ月で)**失効**します。
|
||||
|
||||
その後、Let's Encryptと通信する別のプログラム(別のプログラムである場合もあれば、同じTLS Termination Proxyである場合もある)によって、証明書を更新します。
|
||||
|
||||
<img src="/img/deployment/https/https.drawio.svg">
|
||||
|
||||
**TLS証明書**は、IPアドレスではなく、**ドメイン名に関連付けられて**います。
|
||||
|
||||
したがって、証明書を更新するために、更新プログラムは、認証局(Let's Encrypt)に対して、**そのドメインが本当に「所有」し、管理している**ことを**証明**する必要があります。
|
||||
|
||||
そのために、またさまざまなアプリケーションのニーズに対応するために、いくつかの方法があります。よく使われる方法としては:
|
||||
|
||||
* **いくつかのDNSレコードを修正します。**
|
||||
* これをするためには、更新プログラムはDNSプロバイダーのAPIをサポートする必要があります。したがって、使用しているDNSプロバイダーによっては、このオプションが使える場合もあれば、使えない場合もあります。
|
||||
* ドメインに関連付けられたパブリックIPアドレス上で、(少なくとも証明書取得プロセス中は)**サーバー**として実行します。
|
||||
* 上で述べたように、特定のIPとポートでリッスンできるプロセスは1つだけです。
|
||||
* これは、同じTLS Termination Proxyが証明書の更新処理も行う場合に非常に便利な理由の1つです。
|
||||
* そうでなければ、TLS Termination Proxyを一時的に停止し、証明書を取得するために更新プログラムを起動し、TLS Termination Proxyで証明書を設定し、TLS Termination Proxyを再起動しなければならないかもしれません。TLS Termination Proxyが停止している間はアプリが利用できなくなるため、これは理想的ではありません。
|
||||
|
||||
|
||||
アプリを提供しながらこのような更新処理を行うことは、アプリケーション・サーバー(Uvicornなど)でTLS証明書を直接使用するのではなく、TLS Termination Proxyを使用して**HTTPSを処理する別のシステム**を用意したくなる主な理由の1つです。
|
||||
|
||||
## まとめ
|
||||
|
||||
**HTTPS**を持つことは非常に重要であり、ほとんどの場合、かなり**クリティカル**です。開発者として HTTPS に関わる労力のほとんどは、これらの**概念とその仕組みを理解する**ことです。
|
||||
|
||||
しかし、ひとたび**開発者向けHTTPS**の基本的な情報を知れば、簡単な方法ですべてを管理するために、さまざまなツールを組み合わせて設定することができます。
|
||||
|
||||
次の章では、**FastAPI** アプリケーションのために **HTTPS** をセットアップする方法について、いくつかの具体例を紹介します。🔒
|
||||
@@ -0,0 +1,7 @@
|
||||
# デプロイ
|
||||
|
||||
**FastAPI** 製のアプリケーションは比較的容易にデプロイできます。
|
||||
|
||||
ユースケースや使用しているツールによっていくつかの方法に分かれます。
|
||||
|
||||
次のセクションでより詳しくそれらの方法について説明します。
|
||||
@@ -0,0 +1,85 @@
|
||||
# 手動デプロイ
|
||||
|
||||
**FastAPI** を手動でデプロイすることもできます。
|
||||
|
||||
以下の様なASGI対応のサーバをインストールする必要があります:
|
||||
|
||||
//// tab | Uvicorn
|
||||
|
||||
* <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>, uvloopとhttptoolsを基にした高速なASGIサーバ。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "uvicorn[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`standard` を加えることで、Uvicornがインストールされ、いくつかの推奨される依存関係を利用するようになります。
|
||||
|
||||
これには、`asyncio` の高性能な完全互換品である `uvloop` が含まれ、並行処理のパフォーマンスが大幅に向上します。
|
||||
|
||||
///
|
||||
|
||||
//// tab | Hypercorn
|
||||
|
||||
* <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>, HTTP/2にも対応しているASGIサーバ。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install hypercorn
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
...または、これら以外のASGIサーバ。
|
||||
|
||||
////
|
||||
|
||||
そして、チュートリアルと同様な方法でアプリケーションを起動して下さい。ただし、以下の様に`--reload` オプションは使用しないで下さい:
|
||||
|
||||
//// tab | Uvicorn
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --host 0.0.0.0 --port 80
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Hypercorn
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ hypercorn main:app --bind 0.0.0.0:80
|
||||
|
||||
Running on 0.0.0.0:8080 over http (CTRL + C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
停止した場合に自動的に再起動させるツールを設定したいかもしれません。
|
||||
|
||||
さらに、<a href="https://gunicorn.org/" class="external-link" target="_blank">Gunicorn</a>をインストールして<a href="https://www.uvicorn.dev/#running-with-gunicorn" class="external-link" target="_blank">Uvicornのマネージャーとして使用したり</a>、複数のワーカーでHypercornを使用したいかもしれません。
|
||||
|
||||
ワーカー数などの微調整も行いたいかもしれません。
|
||||
|
||||
しかしこれら全てをやろうとすると、自動的にこれらを行うDockerイメージを使う方が楽かもしれません。
|
||||
@@ -0,0 +1,185 @@
|
||||
# Server Workers - Gunicorn と Uvicorn
|
||||
|
||||
前回のデプロイメントのコンセプトを振り返ってみましょう:
|
||||
|
||||
* セキュリティ - HTTPS
|
||||
* 起動時の実行
|
||||
* 再起動
|
||||
* **レプリケーション(実行中のプロセス数)**
|
||||
* メモリ
|
||||
* 開始前の事前ステップ
|
||||
|
||||
ここまでのドキュメントのチュートリアルでは、おそらくUvicornのような**サーバープログラム**を**単一のプロセス**で実行しています。
|
||||
|
||||
アプリケーションをデプロイする際には、**複数のコア**を利用し、そしてより多くのリクエストを処理できるようにするために、プロセスの**レプリケーション**を持つことを望むでしょう。
|
||||
|
||||
前のチャプターである[デプロイメントのコンセプト](concepts.md){.internal-link target=_blank}にて見てきたように、有効な戦略がいくつかあります。
|
||||
|
||||
ここでは<a href="https://gunicorn.org/" class="external-link" target="_blank">**Gunicorn**</a>が**Uvicornのワーカー・プロセス**を管理する場合の使い方について紹介していきます。
|
||||
|
||||
/// info
|
||||
|
||||
<!-- NOTE: the current version of docker.md is outdated compared to English one. -->
|
||||
DockerやKubernetesなどのコンテナを使用している場合は、次の章で詳しく説明します: [コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}
|
||||
|
||||
特に**Kubernetes**上で実行する場合は、おそらく**Gunicornを使用せず**、**コンテナごとに単一のUvicornプロセス**を実行することになりますが、それについてはこの章の後半で説明します。
|
||||
|
||||
///
|
||||
|
||||
## GunicornによるUvicornのワーカー・プロセスの管理
|
||||
|
||||
**Gunicorn**は**WSGI標準**のアプリケーションサーバーです。このことは、GunicornはFlaskやDjangoのようなアプリケーションにサービスを提供できることを意味します。Gunicornそれ自体は**FastAPI**と互換性がないですが、というのもFastAPIは最新の**<a href="https://asgi.readthedocs.io/en/latest/" class="external-link" target="_blank">ASGI 標準</a>**を使用しているためです。
|
||||
|
||||
しかし、Gunicornは**プロセスマネージャー**として動作し、ユーザーが特定の**ワーカー・プロセスクラス**を使用するように指示することができます。するとGunicornはそのクラスを使い1つ以上の**ワーカー・プロセス**を開始します。
|
||||
|
||||
そして**Uvicorn**には**Gunicorn互換のワーカークラス**があります。
|
||||
|
||||
この組み合わせで、Gunicornは**プロセスマネージャー**として動作し、**ポート**と**IP**をリッスンします。そして、**Uvicornクラス**を実行しているワーカー・プロセスに通信を**転送**します。
|
||||
|
||||
そして、Gunicorn互換の**Uvicornワーカー**クラスが、FastAPIが使えるように、Gunicornから送られてきたデータをASGI標準に変換する役割を担います。
|
||||
|
||||
## GunicornとUvicornをインストールする
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "uvicorn[standard]" gunicorn
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
これによりUvicornと(高性能を得るための)標準(`standard`)の追加パッケージとGunicornの両方がインストールされます。
|
||||
|
||||
## UvicornのワーカーとともにGunicornを実行する
|
||||
|
||||
Gunicornを以下のように起動させることができます:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
|
||||
|
||||
[19499] [INFO] Starting gunicorn 20.1.0
|
||||
[19499] [INFO] Listening at: http://0.0.0.0:80 (19499)
|
||||
[19499] [INFO] Using worker: uvicorn.workers.UvicornWorker
|
||||
[19511] [INFO] Booting worker with pid: 19511
|
||||
[19513] [INFO] Booting worker with pid: 19513
|
||||
[19514] [INFO] Booting worker with pid: 19514
|
||||
[19515] [INFO] Booting worker with pid: 19515
|
||||
[19511] [INFO] Started server process [19511]
|
||||
[19511] [INFO] Waiting for application startup.
|
||||
[19511] [INFO] Application startup complete.
|
||||
[19513] [INFO] Started server process [19513]
|
||||
[19513] [INFO] Waiting for application startup.
|
||||
[19513] [INFO] Application startup complete.
|
||||
[19514] [INFO] Started server process [19514]
|
||||
[19514] [INFO] Waiting for application startup.
|
||||
[19514] [INFO] Application startup complete.
|
||||
[19515] [INFO] Started server process [19515]
|
||||
[19515] [INFO] Waiting for application startup.
|
||||
[19515] [INFO] Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
それぞれのオプションの意味を見てみましょう:
|
||||
|
||||
* `main:app`: `main`は"`main`"という名前のPythonモジュール、つまりファイル`main.py`を意味します。そして `app` は **FastAPI** アプリケーションの変数名です。
|
||||
* main:app`はPythonの`import`文と同じようなものだと想像できます:
|
||||
|
||||
```Python
|
||||
from main import app
|
||||
```
|
||||
|
||||
* つまり、`main:app`のコロンは、`from main import app`のPythonの`import`の部分と同じになります。
|
||||
|
||||
* `--workers`: 使用するワーカー・プロセスの数で、それぞれがUvicornのワーカーを実行します。
|
||||
|
||||
* `--worker-class`: ワーカー・プロセスで使用するGunicorn互換のワーカークラスです。
|
||||
* ここではGunicornがインポートして使用できるクラスを渡します:
|
||||
|
||||
```Python
|
||||
import uvicorn.workers.UvicornWorker
|
||||
```
|
||||
|
||||
* `--bind`: GunicornにリッスンするIPとポートを伝えます。コロン(`:`)でIPとポートを区切ります。
|
||||
* Uvicornを直接実行している場合は、`--bind 0.0.0.0:80` (Gunicornのオプション)の代わりに、`--host 0.0.0.0`と `--port 80`を使います。
|
||||
|
||||
出力では、各プロセスの**PID**(プロセスID)が表示されているのがわかります(単なる数字です)。
|
||||
|
||||
以下の通りです:
|
||||
|
||||
* Gunicornの**プロセス・マネージャー**はPID `19499`(あなたの場合は違う番号でしょう)で始まります。
|
||||
* 次に、`Listening at: http://0.0.0.0:80`を開始します。
|
||||
* それから `uvicorn.workers.UvicornWorker` でワーカークラスを使用することを検出します。
|
||||
* そして、**4つのワーカー**を起動します。それぞれのワーカーのPIDは、`19511`、`19513`、`19514`、`19515`です。
|
||||
|
||||
Gunicornはまた、ワーカーの数を維持するために必要であれば、**ダウンしたプロセス**を管理し、**新しいプロセスを**再起動**させます。そのため、上記のリストにある**再起動**の概念に一部役立ちます。
|
||||
|
||||
しかしながら、必要であればGunicornを**再起動**させ、**起動時に実行**させるなど、外部のコンポーネントを持たせることも必要かもしれません。
|
||||
|
||||
## Uvicornとワーカー
|
||||
|
||||
Uvicornには複数の**ワーカー・プロセス**を起動し実行するオプションもあります。
|
||||
|
||||
とはいうものの、今のところUvicornのワーカー・プロセスを扱う機能はGunicornよりも制限されています。そのため、このレベル(Pythonレベル)でプロセスマネージャーを持ちたいのであれば、Gunicornをプロセスマネージャーとして使ってみた方が賢明かもしれないです。
|
||||
|
||||
どんな場合であれ、以下のように実行します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
|
||||
<font color="#A6E22E">INFO</font>: Uvicorn running on <b>http://0.0.0.0:8080</b> (Press CTRL+C to quit)
|
||||
<font color="#A6E22E">INFO</font>: Started parent process [<font color="#A1EFE4"><b>27365</b></font>]
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27368</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27369</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27370</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27367</font>]
|
||||
<font color="#A6E22E">INFO</font>: Waiting for application startup.
|
||||
<font color="#A6E22E">INFO</font>: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
ここで唯一の新しいオプションは `--workers` で、Uvicornに4つのワーカー・プロセスを起動するように指示しています。
|
||||
|
||||
各プロセスの **PID** が表示され、親プロセスの `27365` (これは **プロセスマネージャ**) と、各ワーカー・プロセスの **PID** が表示されます: `27368`、`27369`、`27370`、`27367`になります。
|
||||
|
||||
## デプロイメントのコンセプト
|
||||
|
||||
ここでは、アプリケーションの実行を**並列化**し、CPUの**マルチコア**を活用し、**より多くのリクエスト**に対応できるようにするために、**Gunicorn**(またはUvicorn)を使用して**Uvicornワーカー・プロセス**を管理する方法を見ていきました。
|
||||
|
||||
上記のデプロイのコンセプトのリストから、ワーカーを使うことは主に**レプリケーション**の部分と、**再起動**を少し助けてくれます:
|
||||
|
||||
* セキュリティ - HTTPS
|
||||
* 起動時の実行
|
||||
* 再起動
|
||||
* レプリケーション(実行中のプロセス数)
|
||||
* メモリー
|
||||
* 開始前の事前のステップ
|
||||
|
||||
|
||||
## コンテナとDocker
|
||||
<!-- NOTE: the current version of docker.md is outdated compared to English one. -->
|
||||
次章の[コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}では、その他の**デプロイのコンセプト**を扱うために実施するであろう戦略をいくつか紹介します。
|
||||
|
||||
また、**GunicornとUvicornワーカー**を含む**公式Dockerイメージ**と、簡単なケースに役立ついくつかのデフォルト設定も紹介します。
|
||||
|
||||
また、(Gunicornを使わずに)Uvicornプロセスを1つだけ実行するために、**ゼロから独自のイメージを**構築する方法も紹介します。これは簡単なプロセスで、おそらく**Kubernetes**のような分散コンテナ管理システムを使うときにやりたいことでしょう。
|
||||
|
||||
## まとめ
|
||||
|
||||
Uvicornワーカーを使ったプロセスマネージャとして**Gunicorn**(またはUvicorn)を使えば、**マルチコアCPU**を活用して**複数のプロセスを並列実行**できます。
|
||||
|
||||
これらのツールやアイデアは、**あなた自身のデプロイシステム**をセットアップしながら、他のデプロイコンセプトを自分で行う場合にも使えます。
|
||||
|
||||
次の章では、コンテナ(DockerやKubernetesなど)を使った**FastAPI**について学んでいきましょう。これらのツールには、他の**デプロイのコンセプト**も解決する簡単な方法があることがわかるでしょう。✨
|
||||
@@ -0,0 +1,93 @@
|
||||
# FastAPIのバージョンについて
|
||||
|
||||
**FastAPI** は既に多くのアプリケーションやシステムに本番環境で使われています。また、100%のテストカバレッジを維持しています。しかし、活発な開発が続いています。
|
||||
|
||||
高頻度で新機能が追加され、定期的にバグが修正され、実装は継続的に改善されています。
|
||||
|
||||
これが現在のバージョンがいまだに `0.x.x` な理由であり、それぞれのバージョンは破壊的な変更がなされる可能性があります。これは、<a href="https://semver.org/" class="external-link" target="_blank">セマンティック バージョニング</a>の規則に則っています。
|
||||
|
||||
**FastAPI** を使用すると本番用アプリケーションをすぐに作成できますが (すでに何度も経験しているかもしれませんが)、残りのコードが正しく動作するバージョンなのか確認しなければいけません。
|
||||
|
||||
## `fastapi` のバージョンを固定
|
||||
|
||||
最初にすべきことは、アプリケーションが正しく動作する **FastAPI** のバージョンを固定することです。
|
||||
|
||||
例えば、バージョン `0.45.0` を使っているとしましょう。
|
||||
|
||||
`requirements.txt` を使っているなら、以下の様にバージョンを指定できます:
|
||||
|
||||
```txt
|
||||
fastapi==0.45.0
|
||||
```
|
||||
|
||||
これは、厳密にバージョン `0.45.0` だけを使うことを意味します。
|
||||
|
||||
または、以下の様に固定することもできます:
|
||||
|
||||
```txt
|
||||
fastapi>=0.45.0,<0.46.0
|
||||
```
|
||||
|
||||
これは `0.45.0` 以上、`0.46.0` 未満のバージョンを使うことを意味します。例えば、バージョン `0.45.2` は使用可能です。
|
||||
|
||||
PoetryやPipenvなど、他のインストール管理ツールを使用している場合でも、それぞれパッケージのバージョンを指定する機能があります。
|
||||
|
||||
## 利用可能なバージョン
|
||||
|
||||
[Release Notes](../release-notes.md){.internal-link target=_blank}で利用可能なバージョンが確認できます (現在の最新版の確認などのため)。
|
||||
|
||||
## バージョンについて
|
||||
|
||||
セマンティック バージョニングの規約に従って、`1.0.0` 未満の全てのバージョンは破壊的な変更が加わる可能性があります。
|
||||
|
||||
FastAPIでは「パッチ」バージョンはバグ修正と非破壊的な変更に留めるという規約に従っています。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
「パッチ」は最後の数字を指します。例えば、`0.2.3` ではパッチバージョンは `3` です。
|
||||
|
||||
///
|
||||
|
||||
従って、以下の様なバージョンの固定が望ましいです:
|
||||
|
||||
```txt
|
||||
fastapi>=0.45.0,<0.46.0
|
||||
```
|
||||
|
||||
破壊的な変更と新機能実装は「マイナー」バージョンで加えられます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
「マイナー」は真ん中の数字です。例えば、`0.2.3` ではマイナーバージョンは `2` です。
|
||||
|
||||
///
|
||||
|
||||
## FastAPIのバージョンのアップグレード
|
||||
|
||||
アプリケーションにテストを加えるべきです。
|
||||
|
||||
**FastAPI** では非常に簡単に実現できます (Starletteのおかげで)。ドキュメントを確認して下さい: [テスト](../tutorial/testing.md){.internal-link target=_blank}
|
||||
|
||||
テストを加えた後で、**FastAPI** のバージョンをより最新のものにアップグレードし、テストを実行することで全てのコードが正常に動作するか確認できます。
|
||||
|
||||
全てが動作するか、修正を行った上で全てのテストを通過した場合、使用している`fastapi` のバージョンをより最新のバージョンに固定できます。
|
||||
|
||||
## Starletteについて
|
||||
|
||||
`Starlette` のバージョンは固定すべきではありません。
|
||||
|
||||
**FastAPI** は、バージョン毎にStarletteのより新しいバージョンを使用します。
|
||||
|
||||
よって、最適なStarletteのバージョン選択を**FastAPI** に任せることができます。
|
||||
|
||||
## Pydanticについて
|
||||
|
||||
Pydanticは自身のテストだけでなく**FastAPI** のためのテストを含んでいます。なので、Pydanticの新たなバージョン ( `1.0.0` 以降) は全てFastAPIと整合性があります。
|
||||
|
||||
Pydanticのバージョンを、動作が保証できる`1.0.0`以降のいずれかのバージョンから`2.0.0` 未満の間に固定できます。
|
||||
|
||||
例えば:
|
||||
|
||||
```txt
|
||||
pydantic>=1.2.0,<2.0.0
|
||||
```
|
||||
@@ -0,0 +1,301 @@
|
||||
# 環境変数
|
||||
|
||||
/// tip
|
||||
|
||||
もし、「環境変数」とは何か、それをどう使うかを既に知っている場合は、このセクションをスキップして構いません。
|
||||
|
||||
///
|
||||
|
||||
環境変数(**env var**とも呼ばれる)はPythonコードの**外側**、つまり**OS**に存在する変数で、Pythonから読み取ることができます。(他のプログラムでも同様に読み取れます。)
|
||||
|
||||
環境変数は、アプリケーションの**設定**の管理や、Pythonの**インストール**などに役立ちます。
|
||||
|
||||
## 環境変数の作成と使用
|
||||
|
||||
環境変数は**シェル(ターミナル)**内で**作成**して使用でき、それらにPythonは不要です。
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// You could create an env var MY_NAME with
|
||||
$ export MY_NAME="Wade Wilson"
|
||||
|
||||
// Then you could use it with other programs, like
|
||||
$ echo "Hello $MY_NAME"
|
||||
|
||||
Hello Wade Wilson
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
|
||||
```console
|
||||
// Create an env var MY_NAME
|
||||
$ $Env:MY_NAME = "Wade Wilson"
|
||||
|
||||
// Use it with other programs, like
|
||||
$ echo "Hello $Env:MY_NAME"
|
||||
|
||||
Hello Wade Wilson
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
## Pythonで環境変数を読み取る
|
||||
|
||||
環境変数をPythonの**外側**、ターミナル(や他の方法)で作成し、**Python内で読み取る**こともできます。
|
||||
|
||||
例えば、以下のような`main.py`ファイルを用意します:
|
||||
|
||||
```Python hl_lines="3"
|
||||
import os
|
||||
|
||||
name = os.getenv("MY_NAME", "World")
|
||||
print(f"Hello {name} from Python")
|
||||
```
|
||||
|
||||
/// tip
|
||||
|
||||
<a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> の第2引数は、デフォルトで返される値を指定します。
|
||||
|
||||
この引数を省略するとデフォルト値として`None`が返されますが、ここではデフォルト値として`"World"`を指定しています。
|
||||
|
||||
///
|
||||
|
||||
次に、このPythonプログラムを呼び出します。
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Here we don't set the env var yet
|
||||
$ python main.py
|
||||
|
||||
// As we didn't set the env var, we get the default value
|
||||
|
||||
Hello World from Python
|
||||
|
||||
// But if we create an environment variable first
|
||||
$ export MY_NAME="Wade Wilson"
|
||||
|
||||
// And then call the program again
|
||||
$ python main.py
|
||||
|
||||
// Now it can read the environment variable
|
||||
|
||||
Hello Wade Wilson from Python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Here we don't set the env var yet
|
||||
$ python main.py
|
||||
|
||||
// As we didn't set the env var, we get the default value
|
||||
|
||||
Hello World from Python
|
||||
|
||||
// But if we create an environment variable first
|
||||
$ $Env:MY_NAME = "Wade Wilson"
|
||||
|
||||
// And then call the program again
|
||||
$ python main.py
|
||||
|
||||
// Now it can read the environment variable
|
||||
|
||||
Hello Wade Wilson from Python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
環境変数はコードの外側で設定し、内側から読み取ることができるので、他のファイルと一緒に(`git`に)保存する必要がありません。そのため、環境変数をコンフィグレーションや**設定**に使用することが一般的です。
|
||||
|
||||
また、**特定のプログラムの呼び出し**のための環境変数を、そのプログラムのみ、その実行中に限定して利用できるよう作成できます。
|
||||
|
||||
そのためには、プログラム起動コマンドと同じコマンドライン上の、起動コマンド直前で環境変数を作成してください。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Create an env var MY_NAME in line for this program call
|
||||
$ MY_NAME="Wade Wilson" python main.py
|
||||
|
||||
// Now it can read the environment variable
|
||||
|
||||
Hello Wade Wilson from Python
|
||||
|
||||
// The env var no longer exists afterwards
|
||||
$ python main.py
|
||||
|
||||
Hello World from Python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// tip
|
||||
|
||||
詳しくは <a href="https://12factor.net/config" class="external-link" target="_blank">The Twelve-Factor App: Config</a> を参照してください。
|
||||
|
||||
///
|
||||
|
||||
## 型とバリデーション
|
||||
|
||||
環境変数は**テキスト文字列**のみを扱うことができます。これは、環境変数がPython外部に存在し、他のプログラムやシステム全体(Linux、Windows、macOS間の互換性を含む)と連携する必要があるためです。
|
||||
|
||||
つまり、Pythonが環境変数から読み取る**あらゆる値**は **`str`型となり**、他の型への変換やバリデーションはコード内で行う必要があります。
|
||||
|
||||
環境変数を使用して**アプリケーション設定**を管理する方法については、[高度なユーザーガイド - Settings and Environment Variables](./advanced/settings.md){.internal-link target=_blank}で詳しく学べます。
|
||||
|
||||
## `PATH`環境変数
|
||||
|
||||
**`PATH`**という**特別な**環境変数があります。この環境変数は、OS(Linux、macOS、Windows)が実行するプログラムを発見するために使用されます。
|
||||
|
||||
`PATH`変数は、複数のディレクトリのパスから成る長い文字列です。このパスはLinuxやMacOSの場合は`:`で、Windowsの場合は`;`で区切られています。
|
||||
|
||||
例えば、`PATH`環境変数は次のような文字列かもしれません:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
```plaintext
|
||||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
これは、OSはプログラムを見つけるために以下のディレクトリを探す、ということを意味します:
|
||||
|
||||
* `/usr/local/bin`
|
||||
* `/usr/bin`
|
||||
* `/bin`
|
||||
* `/usr/sbin`
|
||||
* `/sbin`
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
```plaintext
|
||||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
|
||||
```
|
||||
|
||||
これは、OSはプログラムを見つけるために以下のディレクトリを探す、ということを意味します:
|
||||
|
||||
* `C:\Program Files\Python312\Scripts`
|
||||
* `C:\Program Files\Python312`
|
||||
* `C:\Windows\System32`
|
||||
|
||||
////
|
||||
|
||||
ターミナル上で**コマンド**を入力すると、 OSはそのプログラムを見つけるために、`PATH`環境変数のリストに記載された**それぞれのディレクトリを探し**ます。
|
||||
|
||||
例えば、ターミナル上で`python`を入力すると、OSは`python`によって呼ばれるプログラムを見つけるために、そのリストの**先頭のディレクトリ**を最初に探します。
|
||||
|
||||
OSは、もしそのプログラムをそこで発見すれば**実行し**ますが、そうでなければリストの**他のディレクトリ**を探していきます。
|
||||
|
||||
### PythonのインストールとPATH環境変数の更新
|
||||
|
||||
Pythonのインストール時に`PATH`環境変数を更新したいか聞かれるかもしれません。
|
||||
|
||||
/// tab | Linux, macOS
|
||||
|
||||
Pythonをインストールして、そのプログラムが`/opt/custompython/bin`というディレクトリに配置されたとします。
|
||||
|
||||
もし、`PATH`環境変数を更新するように答えると、`PATH`環境変数に`/opt/custompython/bin`が追加されます。
|
||||
|
||||
`PATH`環境変数は以下のように更新されるでしょう:
|
||||
|
||||
``` plaintext
|
||||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
|
||||
```
|
||||
|
||||
このようにして、ターミナルで`python`と入力したときに、OSは`/opt/custompython/bin`(リストの末尾のディレクトリ)にあるPythonプログラムを見つけ、使用します。
|
||||
|
||||
///
|
||||
|
||||
/// tab | Windows
|
||||
|
||||
Pythonをインストールして、そのプログラムが`C:\opt\custompython\bin`というディレクトリに配置されたとします。
|
||||
|
||||
もし、`PATH`環境変数を更新するように答えると、`PATH`環境変数に`C:\opt\custompython\bin`が追加されます。
|
||||
|
||||
`PATH`環境変数は以下のように更新されるでしょう:
|
||||
|
||||
```plaintext
|
||||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
|
||||
```
|
||||
|
||||
このようにして、ターミナルで`python`と入力したときに、OSは`C:\opt\custompython\bin\python`(リストの末尾のディレクトリ)にあるPythonプログラムを見つけ、使用します。
|
||||
|
||||
///
|
||||
|
||||
つまり、ターミナルで以下のコマンドを入力すると:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
``` console
|
||||
$ python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// tab | Linux, macOS
|
||||
|
||||
OSは`/opt/custompython/bin`にある`python`プログラムを**見つけ**て実行します。
|
||||
|
||||
これは、次のコマンドを入力した場合とほとんど同等です:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ /opt/custompython/bin/python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
///
|
||||
|
||||
/// tab | Windows
|
||||
|
||||
OSは`C:\opt\custompython\bin\python`にある`python`プログラムを**見つけ**て実行します。
|
||||
|
||||
これは、次のコマンドを入力した場合とほとんど同等です:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ C:\opt\custompython\bin\python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
///
|
||||
|
||||
この情報は、[Virtual Environments](virtual-environments.md) について学ぶ際にも役立ちます。
|
||||
|
||||
## まとめ
|
||||
|
||||
これで、**環境変数**とは何か、Pythonでどのように使用するかについて、基本的な理解が得られたはずです。
|
||||
|
||||
環境変数についての詳細は、<a href="https://en.wikipedia.org/wiki/Environment_variable" class="external-link" target="_blank">Wikipedia: Environment Variable</a> を参照してください。
|
||||
|
||||
環境変数の用途や適用方法が最初は直感的ではないかもしれませんが、開発中のさまざまなシナリオで繰り返し登場します。そのため、基本を知っておくことが重要です。
|
||||
|
||||
たとえば、この情報は次のセクションで扱う[Virtual Environments](virtual-environments.md)にも関連します。
|
||||
@@ -0,0 +1,204 @@
|
||||
# 機能
|
||||
|
||||
## FastAPIの機能
|
||||
|
||||
**FastAPI** は以下の機能をもちます:
|
||||
|
||||
### オープンスタンダード準拠
|
||||
|
||||
* API作成のための<a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a>。これは、<abbr title="also known as: endpoints, routes">path</abbr> <abbr title="also known as HTTP methods, as POST, GET, PUT, DELETE">operations</abbr>の宣言、パラメータ、ボディリクエスト、セキュリティなどを含んでいます。
|
||||
* <a href="http://json-schema.org/" class="external-link" target="_blank"><strong>JSONスキーマ</strong></a>を使用したデータモデルのドキュメント自動生成(OpenAPIはJSONスキーマに基づいている)。
|
||||
* 綿密な調査の結果、上層に後付けするのではなく、これらの基準に基づいて設計されました。
|
||||
* これにより、多くの言語で自動 **クライアントコード生成** が可能です。
|
||||
|
||||
### 自動ドキュメント生成
|
||||
対話的なAPIドキュメントと探索的なwebユーザーインターフェースを提供します。フレームワークはOpenAPIを基にしているため、いくつかのオプションがあり、デフォルトで2つ含まれています。
|
||||
|
||||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>で、インタラクティブな探索をしながら、ブラウザから直接APIを呼び出してテストが行えます。
|
||||
|
||||

|
||||
|
||||
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>を使用したもう一つのAPIドキュメント生成。
|
||||
|
||||

|
||||
|
||||
### 現代的なPython
|
||||
|
||||
FastAPIの機能はすべて、標準のPython 3.8型宣言に基づいています(Pydanticの功績)。新しい構文はありません。ただの現代的な標準のPythonです。
|
||||
|
||||
(FastAPIを使用しない場合でも)Pythonの型の使用方法について簡単な復習が必要な場合は、短いチュートリアル([Python Types](python-types.md){.internal-link target=_blank})を参照してください。
|
||||
|
||||
型を使用した標準的なPythonを記述します:
|
||||
|
||||
```Python
|
||||
from datetime import date
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Declare a variable as a str
|
||||
# and get editor support inside the function
|
||||
def main(user_id: str):
|
||||
return user_id
|
||||
|
||||
|
||||
# A Pydantic model
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
joined: date
|
||||
```
|
||||
|
||||
これは以下のように用いられます:
|
||||
|
||||
```Python
|
||||
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
|
||||
|
||||
second_user_data = {
|
||||
"id": 4,
|
||||
"name": "Mary",
|
||||
"joined": "2018-11-30",
|
||||
}
|
||||
|
||||
my_second_user: User = User(**second_user_data)
|
||||
```
|
||||
|
||||
/// info | 情報
|
||||
|
||||
`**second_user_data` は以下を意味します:
|
||||
|
||||
`second_user_data`辞書のキーと値を直接、キーと値の引数として渡します。これは、`User(id=4, name="Mary", joined="2018-11-30")`と同等です。
|
||||
|
||||
///
|
||||
|
||||
### エディタのサポート
|
||||
|
||||
すべてのフレームワークは使いやすく直感的に使用できるように設計されており、すべての決定は開発を開始する前でも複数のエディターでテストされ、最高の開発体験が保証されます。
|
||||
|
||||
前回のPython開発者調査では、<a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">最も使用されている機能が「オートコンプリート」であることが明らかになりました。</a>
|
||||
|
||||
**FastAPI** フレームワークは、この要求を満たすことを基本としています。オートコンプリートはどこでも機能します。
|
||||
|
||||
ドキュメントに戻る必要はほとんどありません。
|
||||
|
||||
エディターがどのように役立つかを以下に示します:
|
||||
|
||||
* <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>の場合:
|
||||
|
||||

|
||||
|
||||
* <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>の場合:
|
||||
|
||||

|
||||
|
||||
以前は不可能だと考えていたコードでさえ補完されます。例えば、リクエストからのJSONボディ(ネストされている可能性がある)内の `price`キーです。
|
||||
|
||||
間違ったキー名を入力したり、ドキュメント間を行き来したり、上下にスクロールして`username`と`user_name`のどちらを使用したか調べたりする必要はもうありません。
|
||||
|
||||
### 簡潔
|
||||
|
||||
すべてに適切な**デフォルト**があり、オプションの構成ができます。必要なことを実行し、必要なAPIを定義するためにすべてのパラメーターを調整できます。
|
||||
|
||||
ただし、デフォルトでもすべて **うまくいきます**。
|
||||
|
||||
### 検証
|
||||
|
||||
* 以下の様な、ほとんどの(すべての?)Python **データ型**の検証:
|
||||
* JSONオブジェクト(`dict`)
|
||||
* 項目の型を定義するJSON配列(`list`)
|
||||
* 最小長と最大長のある文字列(`str`)フィールド
|
||||
* 最小値と最大値のある数値(`int`、` float`)
|
||||
|
||||
* よりエキゾチックな型の検証:
|
||||
* URL
|
||||
* Eメール
|
||||
* UUID
|
||||
* ...その他
|
||||
|
||||
すべての検証は、確立された堅牢な **Pydantic** によって処理されます。
|
||||
|
||||
### セキュリティと認証
|
||||
|
||||
セキュリティと認証が統合されています。 データベースまたはデータモデルについても妥協していません。
|
||||
|
||||
以下のOpenAPIで定義されているすべてのセキュリティスキームを含む:
|
||||
|
||||
* HTTPベーシック
|
||||
* **OAuth2**(**JWTトークン**も使用)。 JWTを使用したOAuth2のチュートリアル([OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank})を確認してください。
|
||||
* APIキー:
|
||||
* ヘッダー
|
||||
* クエリパラメータ
|
||||
* クッキー、等
|
||||
|
||||
さらに、Starletteのすべてのセキュリティ機能も含みます(**セッションCookie**を含む)。
|
||||
|
||||
これらは、システム、データストア、リレーショナルデータベース、NoSQLデータベースなどと簡単に統合できる再利用可能なツールとコンポーネントとして構築されています。
|
||||
|
||||
### 依存性の注入(Dependency Injection)
|
||||
|
||||
FastAPIには非常に使いやすく、非常に強力な<abbr title='also known as "components", "resources", "services", "providers"'><strong>依存性の注入</strong></abbr>システムを備えています。
|
||||
|
||||
* 依存関係でさえも依存関係を持つことができ、階層または **依存関係の"グラフ"** を作成することができます。
|
||||
|
||||
* フレームワークによってすべて**自動的に処理**されます。
|
||||
* すべての依存関係はリクエストからのデータを要請できて、**path operationsの制約と自動ドキュメンテーションを拡張できます**。
|
||||
* 依存関係で定義された *path operation* パラメータも**自動検証**が可能です。
|
||||
* 複雑なユーザー認証システム、**データベース接続**などのサポート
|
||||
* **データベース、フロントエンドなどに対する妥協はありません**。それらすべてと簡単に統合できます。
|
||||
|
||||
### 無制限の「プラグイン」
|
||||
|
||||
他の方法では、それらを必要とせず、必要なコードをインポートして使用します。
|
||||
|
||||
統合は非常に簡単に使用できるように設計されており(依存関係を用いて)、*path operations* で使用されているのと同じ構造と構文を使用して、2行のコードでアプリケーションの「プラグイン」を作成できます。
|
||||
|
||||
|
||||
### テスト
|
||||
|
||||
* <abbr title = "自動的にテストされるコードの量">テストカバレッジ</abbr> 100%
|
||||
* <abbr title = "Python型アノテーション。これにより、ユーザーはより良いエディターと外部ツールのサポート受けられる。">型アノテーション</abbr>100%のコードベース
|
||||
* 本番アプリケーションで使用されます
|
||||
|
||||
## Starletteの機能
|
||||
|
||||
**FastAPI**は、<a href="https://www.starlette.dev/" class="external-link" target="_blank"><strong>Starlette </strong></a>と完全に互換性があります(そしてベースになっています)。したがって、追加のStarletteコードがあれば、それも機能します。
|
||||
|
||||
`FastAPI`は実際には`Starlette`のサブクラスです。したがって、Starletteをすでに知っているか使用している場合は、ほとんどの機能が同じように機能します。
|
||||
|
||||
**FastAPI**を使用すると、以下のような、**Starlette**のすべての機能を利用できます(FastAPIはStarletteを強化したものにすぎないため):
|
||||
|
||||
* 見事なパフォーマンス。<a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank"> **NodeJS**および**Go**に匹敵する、最速のPythonフレームワークの1つです。</a>
|
||||
|
||||
* **WebSocket**のサポート
|
||||
* **GraphQL**のサポート
|
||||
* プロセス内バックグラウンドタスク
|
||||
* 起動およびシャットダウンイベント
|
||||
* `httpx`に基づいて構築されたテストクライアント
|
||||
* **CORS**、GZip、静的ファイル、ストリーミング応答
|
||||
* **セッションとCookie**のサポート
|
||||
* テストカバレッジ100%
|
||||
* 型アノテーション100%のコードベース
|
||||
|
||||
## Pydanticの特徴
|
||||
|
||||
**FastAPI**は<a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic </strong></a>と完全に互換性があります(そしてベースになっています)。したがって、追加のPydanticコードがあれば、それも機能します。
|
||||
|
||||
データベースのために<abbr title = "Object-Relational Mapper">ORM</abbr>sや、<abbr title = "Object-Document Mapper">ODM</abbr>sなどの、Pydanticに基づく外部ライブラリを備えています。
|
||||
|
||||
これは、すべてが自動的に検証されるため、多くの場合、リクエストから取得したオブジェクトを**データベースに直接**渡すことができるということを意味しています。
|
||||
|
||||
同じことがその逆にも当てはまり、多くの場合、データベースから取得したオブジェクトを**クライアントに直接**渡すことができます。
|
||||
|
||||
**FastAPI**を使用すると、**Pydantic**のすべての機能を利用できます(FastAPIがPydanticに基づいてすべてのデータ処理を行っているため)。
|
||||
|
||||
* **brainfuckなし**:
|
||||
* スキーマ定義のためのマイクロ言語を新たに学習する必要はありません。
|
||||
* Pythonの型を知っている場合は、既にPydanticの使用方法を知っているに等しいです。
|
||||
* ユーザーの **<abbr title = "コードエディターに似た統合開発環境">IDE</abbr>/<abbr title = "コードエラーをチェックするプログラム">リンター</abbr>/思考 とうまく連携します**:
|
||||
* Pydanticのデータ構造は、ユーザーが定義するクラスの単なるインスタンスであるため、オートコンプリート、リンティング、mypy、およびユーザーの直感はすべて、検証済みのデータで適切に機能するはずです。
|
||||
* **複雑な構造**を検証:
|
||||
* 階層的なPydanticモデルや、Pythonの「`typing`」の「`list`」と「`dict`」などの利用。
|
||||
* バリデーターにより、複雑なデータスキーマを明確かつ簡単に定義、チェックし、JSONスキーマとして文書化できます。
|
||||
* 深く**ネストされたJSON**オブジェクトを作成し、それらすべてを検証してアノテーションを付けることができます。
|
||||
* **拡張可能**:
|
||||
* Pydanticでは、カスタムデータ型を定義できます。または、バリデーターデコレーターで装飾されたモデルのメソッドを使用して検証を拡張できます。
|
||||
* テストカバレッジ 100%。
|
||||
@@ -0,0 +1,102 @@
|
||||
# FastAPIを応援 - ヘルプの入手
|
||||
|
||||
**FastAPI** は気に入りましたか?
|
||||
|
||||
FastAPIやユーザーや開発者を応援したいですか?
|
||||
|
||||
もしくは、 **FastAPI** についてヘルプが必要ですか?
|
||||
|
||||
とても簡単に応援できます (ただ1、2回クリックするだけのものもあります)。
|
||||
|
||||
また、ヘルプを入手する手段がいくつかあります。
|
||||
|
||||
## GitHubで **FastAPI** にStar
|
||||
|
||||
GitHubでFastAPIに「Star」をつけることができます (右上部のStarボタンをクリック): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. ⭐️
|
||||
|
||||
スターを増やすことで、他のユーザーの目につきやすくなり、多くの人にとって便利なものであることを示せます。
|
||||
|
||||
## GitHubレポジトリのリリースをWatch
|
||||
|
||||
GitHubでFastAPIを「Watch」できます (右上部のWatchボタンをクリック): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
|
||||
|
||||
そこで「Releases only」を選択できます。
|
||||
|
||||
これを行うと、**FastAPI** バグ修正や新機能の実装などの新しいリリース (新しいバージョン) があるたびに (メールで) 通知を受け取れます。
|
||||
|
||||
## 開発者とつながる
|
||||
|
||||
以下で、<a href="https://tiangolo.com" class="external-link" target="_blank">開発者 (Sebastián Ramírez / `tiangolo`)</a> とコンタクトをとれます:
|
||||
|
||||
* <a href="https://github.com/tiangolo" class="external-link" target="_blank">**GitHub** でフォロー</a>。
|
||||
* 他のオープンソースプロジェクトを確認できます。何かの助けになるものが見つかるかもしれません。
|
||||
* 新たなオープンソースプロジェクトを作成したときに通知されます。
|
||||
* <a href="https://x.com/tiangolo" class="external-link" target="_blank">**X (Twitter)** でフォロー</a>。
|
||||
* FastAPIの使用用途を教えてください (聞いてみたいです)。
|
||||
* 新たなツールの発表やリリースが聞けます。
|
||||
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">**Linkedin** でつながる</a>。
|
||||
* 新たなツールの発表やリリースが聞けます (ただしX (Twitter)の方が利用頻度が高いですが 🤷♂)。
|
||||
* <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> や <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a> で著作物を読む (またはフォロー)。
|
||||
* アイデアや作成ツールについての記事が読めます。
|
||||
* 新規記事の執筆を通知してくれます。
|
||||
|
||||
## **FastAPI** に関するツイート
|
||||
|
||||
<a href="https://x.com/compose/tweet?text=I'm loving FastAPI because... https://github.com/fastapi/fastapi cc @tiangolo" class="external-link" target="_blank">**FastAPI** についてツイート</a>し、開発者や他の人にどこが気に入ったのか教えてください。🎉
|
||||
|
||||
**FastAPI** がどのように使われ、どこが気に入られ、どんなプロジェクト/会社で使われているかなどについて知りたいです。
|
||||
|
||||
## FastAPIに投票
|
||||
|
||||
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Slantで **FastAPI** に投票</a>
|
||||
* <a href="https://alternativeto.net/software/fastapi/" class="external-link" target="_blank">AlternativeToで **FastAPI** に投票</a>
|
||||
* <a href="https://github.com/marmelab/awesome-rest/pull/93" class="external-link" target="_blank">awesome-restで **FastAPI** に投票</a>
|
||||
|
||||
## GitHub issuesで他の人を助ける
|
||||
|
||||
<a href="https://github.com/fastapi/fastapi/issues" class="external-link" target="_blank">既存のissues</a>を確認して、他の人を助けてみてください。皆さんが回答を知っているかもしれない質問がほとんどです。🤓
|
||||
|
||||
## GitHubレポジトリをWatch
|
||||
|
||||
GitHubでFastAPIを「watch」できます (右上部の「watch」ボタンをクリック): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
|
||||
|
||||
「Releases only」ではなく「Watching」を選択すると、新たなissueが立てられた際に通知されます。
|
||||
|
||||
そして、issueを解決し他の人を助けることができます。
|
||||
|
||||
## issuesを立てる
|
||||
|
||||
GitHubレポジトリで<a href="https://github.com/fastapi/fastapi/issues/new/choose" class="external-link" target="_blank">新たなissueを立てられます</a>。例えば:
|
||||
|
||||
* 質問、または、問題の報告
|
||||
* 新機能の提案
|
||||
|
||||
**Note**: issueを立てた人は、他の人の手助けもお願いします。😉
|
||||
|
||||
## プルリクエストをする
|
||||
|
||||
以下の様な<a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">プルリクエストを作成</a>できます:
|
||||
|
||||
* ドキュメントのタイプミスを修正。
|
||||
* 新たなドキュメントセクションを提案。
|
||||
* 既存のissue/バグを修正。
|
||||
* 新機能を追加。
|
||||
|
||||
## 開発者のスポンサーになる
|
||||
|
||||
<a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>を通して開発者を経済的にサポートできます。
|
||||
|
||||
そこで、感謝の気持ちを伝えるためにコーヒー☕️を買うことができます 😄。
|
||||
|
||||
## FastAPIを強化するツールのスポンサーになる
|
||||
|
||||
ドキュメントで見たように、FastAPIはStarletteとPydanticという巨人の肩に乗っています。
|
||||
|
||||
以下のスポンサーになることもできます:
|
||||
|
||||
* <a href="https://github.com/sponsors/samuelcolvin" class="external-link" target="_blank">Samuel Colvin (Pydantic)</a>
|
||||
* <a href="https://github.com/sponsors/encode" class="external-link" target="_blank">Encode (Starlette, Uvicorn)</a>
|
||||
|
||||
---
|
||||
|
||||
Thanks! 🚀
|
||||
@@ -0,0 +1,80 @@
|
||||
# 歴史、設計、そしてこれから
|
||||
|
||||
少し前に、<a href="https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920" class="external-link" target="_blank">**FastAPI**
|
||||
のユーザーに以下の様に尋ねられました</a>:
|
||||
|
||||
> このプロジェクトの歴史は?何もないところから、数週間ですごいものができているようです。 [...]
|
||||
|
||||
これがその歴史のほんの一部です。
|
||||
|
||||
## 代替手段
|
||||
|
||||
数年前から、私は複雑な要件を持つAPI (機械学習、分散システム、非同期ジョブ、NoSQLデータベースなど) を作成しており、いくつかの開発者チームを率いています。
|
||||
|
||||
その一環で、多くの方法を調査し、テストし、利用する必要がありました。
|
||||
|
||||
**FastAPI** の歴史は、その前身の歴史が大部分を占めています。
|
||||
|
||||
[代替ツールから受けたインスピレーションと比較](alternatives.md){.internal-link target=_blank}のセクションでこう述べています:
|
||||
|
||||
<blockquote markdown="1">
|
||||
|
||||
**FastAPI**は、代替ツールのこれまでの働きがなければ存在しなかったでしょう。
|
||||
|
||||
以前に作られた多くのツールが、作成における刺激として役立ってきました。
|
||||
|
||||
私は数年前から新しいフレームワークの作成を避けてきました。まず、**FastAPI**でカバーされているすべての機能を、さまざまなフレームワーク、プラグイン、ツールを使って解決しようとしました。
|
||||
|
||||
しかし、その時点では、これらの機能をすべて提供し、以前のツールから優れたアイデアを取り入れ、可能な限り最高の方法でそれらを組み合わせ、それまで利用できなかった言語機能 (Python 3.6以降の型ヒント) を利用したものを作る以外に選択肢はありませんでした。
|
||||
|
||||
</blockquote>
|
||||
|
||||
## 調査
|
||||
|
||||
すべて既存の代替手段を使うことで、そのすべてを学び、アイデアを得て、自分や一緒に仕事をしてきた開発者のチームにとって最良の方法で組み合わせる機会を得ました。
|
||||
|
||||
たとえば、理想的にはPythonの標準的な型ヒントに基づくべきであることが明らかになりました。
|
||||
|
||||
また、すでにある規格を利用するのがベストな方法でした。
|
||||
|
||||
そこで、**FastAPI**のコードを書き始める前に、OpenAPI、JSON Schema、OAuth2などの仕様を数ヶ月かけて勉強し、それらの関係、重複する箇所、相違点を理解しました。
|
||||
|
||||
## 設計
|
||||
|
||||
その後、 (FastAPIを使う開発者として) ユーザーが欲しい「API」の設計に時間を費やしました。
|
||||
|
||||
もっとも人気のあるPythonエディターでいくつかのアイデアをテストしました。PyCharm、VS Code、Jediベースのエディターです。
|
||||
|
||||
最新の <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">Python開発者調査</a>で、それらのエディターがユーザーの80%をカバーしていました。
|
||||
|
||||
これは、**FastAPI**がPython開発者の80%が使用しているエディターで特別にテストされたことを意味します。また、ほとんどの他のエディターも同様に動作する傾向があるため、この恩恵は事実上すべてのエディターでうけられるはずです。
|
||||
|
||||
そうすることで、コードの重複を可能な限り減らし、どこでも補完があるようにし、タイプチェックやエラーチェックなどを実現する最善の方法を見つけました。
|
||||
|
||||
すべての箇所で、すべての開発者に最高の開発体験を提供しました。
|
||||
|
||||
## 要件
|
||||
|
||||
いくつかの代替手法を試したあと、私は<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">**Pydantic**</a>の強みを利用することを決めました。
|
||||
|
||||
そして、JSON Schemaに完全に準拠するようにしたり、制約宣言を定義するさまざまな方法をサポートしたり、いくつかのエディターでのテストに基づいてエディターのサポート (型チェック、自動補完) を改善するために貢献しました。
|
||||
|
||||
開発中、もう1つの重要な鍵となる<a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a>、にも貢献しました。
|
||||
|
||||
## 開発
|
||||
|
||||
私が**FastAPI**自体の作成を開始した時には、ほとんどの部分がすでに準備されており、設計が定義され、必要な条件とツールの準備ができていました。そして規格や仕様に関する知識が、明確になり、更新されていました。
|
||||
|
||||
## これから
|
||||
|
||||
この時点ですでに、これらのアイデアを持った**FastAPI**が多くの人の役に立っていることは明らかです。
|
||||
|
||||
多くのユースケースに適しているため、既存の方法よりも選ばれています。
|
||||
|
||||
多くの開発者やチームが、すでに **FastAPI** にプロジェクトを依存しています (私と私のチームも含めて) 。
|
||||
|
||||
しかし、まだまだ多くの改善点や機能があります。
|
||||
|
||||
**FastAPI**には大きな未来が待っています。
|
||||
|
||||
そして、[あなたの助け](help-fastapi.md){.internal-link target=_blank}を大いに歓迎します。
|
||||
@@ -0,0 +1,56 @@
|
||||
# 条件付き OpenAPI
|
||||
|
||||
必要であれば、設定と環境変数を利用して、環境に応じて条件付きでOpenAPIを構成することが可能です。また、完全にOpenAPIを無効にすることもできます。
|
||||
|
||||
## セキュリティとAPI、およびドキュメントについて
|
||||
|
||||
本番環境においてドキュメントのUIを非表示にすることによって、APIを保護しようと *すべきではありません*。
|
||||
|
||||
それは、APIのセキュリティの強化にはならず、*path operations* は依然として利用可能です。
|
||||
|
||||
もしセキュリティ上の欠陥がソースコードにあるならば、それは存在したままです。
|
||||
|
||||
ドキュメンテーションを非表示にするのは、単にあなたのAPIへのアクセス方法を難解にするだけでなく、同時にあなた自身の本番環境でのAPIのデバッグを困難にしてしまう可能性があります。単純に、 <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">Security through obscurity</a> の一つの形態として考えられるでしょう。
|
||||
|
||||
もしあなたのAPIのセキュリティを強化したいなら、いくつかのよりよい方法があります。例を示すと、
|
||||
|
||||
* リクエストボディとレスポンスのためのPydanticモデルの定義を見直す。
|
||||
* 依存関係に基づきすべての必要なパーミッションとロールを設定する。
|
||||
* パスワードを絶対に平文で保存しない。パスワードハッシュのみを保存する。
|
||||
* PasslibやJWTトークンに代表される、よく知られた暗号化ツールを使って実装する。
|
||||
* そして必要なところでは、もっと細かいパーミッション制御をOAuth2スコープを使って行う。
|
||||
* など
|
||||
|
||||
それでも、例えば本番環境のような特定の環境のみで、あるいは環境変数の設定によってAPIドキュメントをどうしても無効にしたいという、非常に特殊なユースケースがあるかもしれません。
|
||||
|
||||
## 設定と環境変数による条件付き OpenAPI
|
||||
|
||||
生成するOpenAPIとドキュメントUIの構成は、共通のPydanticの設定を使用して簡単に切り替えられます。
|
||||
|
||||
例えば、
|
||||
|
||||
{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
|
||||
|
||||
ここでは `openapi_url` の設定を、デフォルトの `"/openapi.json"` のまま宣言しています。
|
||||
|
||||
そして、これを `FastAPI` appを作る際に使います。
|
||||
|
||||
それから、以下のように `OPENAPI_URL` という環境変数を空文字列に設定することによってOpenAPI (UIドキュメントを含む) を無効化することができます。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ OPENAPI_URL= uvicorn main:app
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
すると、以下のように `/openapi.json`, `/docs`, `/redoc` のどのURLにアクセスしても、 `404 Not Found` エラーが返ってくるようになります。
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Not Found"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,462 @@
|
||||
# FastAPI
|
||||
|
||||
<style>
|
||||
.md-content .md-typeset h1 { display: none; }
|
||||
</style>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**ドキュメント**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
|
||||
**ソースコード**: <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
|
||||
|
||||
---
|
||||
|
||||
FastAPI は、Pythonの標準である型ヒントに基づいてPython 以降でAPI を構築するための、モダンで、高速(高パフォーマンス)な、Web フレームワークです。
|
||||
|
||||
主な特徴:
|
||||
|
||||
- **高速**: **NodeJS** や **Go** 並みのとても高いパフォーマンス (Starlette と Pydantic のおかげです)。 [最も高速な Python フレームワークの一つです](#_10).
|
||||
|
||||
- **高速なコーディング**: 開発速度を約 200%~300%向上させます。 \*
|
||||
- **少ないバグ**: 開発者起因のヒューマンエラーを約 40%削減します。 \*
|
||||
- **直感的**: 素晴らしいエディタのサポートや <abbr title="also known as auto-complete, autocompletion, IntelliSense">オートコンプリート。</abbr> デバッグ時間を削減します。
|
||||
- **簡単**: 簡単に利用、習得できるようにデザインされています。ドキュメントを読む時間を削減します。
|
||||
- **短い**: コードの重複を最小限にしています。各パラメータからの複数の機能。少ないバグ。
|
||||
- **堅牢性**: 自動対話ドキュメントを使用して、本番環境で使用できるコードを取得します。
|
||||
- **Standards-based**: API のオープンスタンダードに基づいており、完全に互換性があります: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (以前は Swagger として知られていました) や <a href="https://json-schema.org/" class="external-link" target="_blank">JSON スキーマ</a>.
|
||||
|
||||
<small>\* 本番アプリケーションを構築している開発チームのテストによる見積もり。</small>
|
||||
|
||||
## Sponsors
|
||||
|
||||
<!-- sponsors -->
|
||||
|
||||
{% if sponsors %}
|
||||
{% for sponsor in sponsors.gold -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor -%}
|
||||
{%- for sponsor in sponsors.silver -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<!-- /sponsors -->
|
||||
|
||||
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
|
||||
|
||||
## 評価
|
||||
|
||||
"_[...] 最近 **FastAPI** を使っています。 [...] 実際に私のチームの全ての **Microsoft の機械学習サービス** で使用する予定です。 そのうちのいくつかのコアな**Windows**製品と**Office**製品に統合されつつあります。_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/fastapi/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_FastAPIライブラリを採用し、クエリで**予測値**を取得できる**REST**サーバを構築しました。 [for Ludwig]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_**Netflix** は、**危機管理**オーケストレーションフレームワーク、**Dispatch**のオープンソースリリースを発表できることをうれしく思います。 [built with **FastAPI**]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_私は**FastAPI**にワクワクしています。 めちゃくちゃ楽しいです!_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_正直、超堅実で洗練されているように見えます。いろんな意味で、それは私がハグしたかったものです。_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://github.com/hugapi/hug" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_REST API を構築するための**モダンなフレームワーク**を学びたい方は、**FastAPI** [...] をチェックしてみてください。 [...] 高速で, 使用、習得が簡単です。[...]_"
|
||||
|
||||
"_私たちの**API**は**FastAPI**に切り替えました。[...] きっと気に入ると思います。 [...]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://x.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://x.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
## **Typer**, the FastAPI of CLIs
|
||||
|
||||
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
|
||||
|
||||
もし Web API の代わりにターミナルで使用する<abbr title="Command Line Interface">CLI</abbr>アプリを構築する場合は、<a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>を確認してください。
|
||||
|
||||
**Typer**は FastAPI の弟分です。そして、**CLI 版 の FastAPI**を意味しています。
|
||||
|
||||
## 必要条件
|
||||
|
||||
FastAPI は巨人の肩の上に立っています。
|
||||
|
||||
- Web の部分は<a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a>
|
||||
- データの部分は<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>
|
||||
|
||||
## インストール
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install fastapi
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
本番環境では、<a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a> または、 <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>のような、 ASGI サーバーが必要になります。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "uvicorn[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## アプリケーション例
|
||||
|
||||
### アプリケーションの作成
|
||||
|
||||
- `main.py` を作成し、以下のコードを入力します:
|
||||
|
||||
```Python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
<details markdown="1">
|
||||
<summary>または<code>async def</code>を使います...</summary>
|
||||
|
||||
`async` / `await`を使用するときは、 `async def`を使います:
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
**注**:
|
||||
|
||||
わからない場合は、<a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">ドキュメントの`async` と `await`にある</a>"In a hurry?"セクションをチェックしてください。
|
||||
|
||||
</details>
|
||||
|
||||
### 実行
|
||||
|
||||
以下のコマンドでサーバーを起動します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
INFO: Started reloader process [28720]
|
||||
INFO: Started server process [28722]
|
||||
INFO: Waiting for application startup.
|
||||
INFO: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<details markdown="1">
|
||||
<summary><code>uvicorn main:app --reload</code>コマンドについて</summary>
|
||||
|
||||
`uvicorn main:app`コマンドは以下の項目を参照します:
|
||||
|
||||
- `main`: `main.py`ファイル (Python "モジュール")
|
||||
- `app`: `main.py` の`app = FastAPI()`の行で生成されたオブジェクト
|
||||
- `--reload`: コードを変更したらサーバーを再起動します。このオプションは開発環境でのみ使用します
|
||||
|
||||
</details>
|
||||
|
||||
### 動作確認
|
||||
|
||||
ブラウザから<a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>を開きます。
|
||||
|
||||
以下の JSON のレスポンスが確認できます:
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
もうすでに以下の API が作成されています:
|
||||
|
||||
- `/` と `/items/{item_id}`のパスで HTTP リクエストを受けます。
|
||||
- どちらのパスも `GET` <em>操作</em> を取ります。(HTTP メソッドとしても知られています。)
|
||||
- `/items/{item_id}` パスのパスパラメータ `item_id` は `int` でなければなりません。
|
||||
- パス `/items/{item_id}` はオプションの `str` クエリパラメータ `q` を持ちます。
|
||||
|
||||
### 自動対話型の API ドキュメント
|
||||
|
||||
<a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>にアクセスしてみてください。
|
||||
|
||||
自動対話型の API ドキュメントが表示されます。 (<a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>が提供しています。):
|
||||
|
||||

|
||||
|
||||
### 代替の API ドキュメント
|
||||
|
||||
<a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>にアクセスしてみてください。
|
||||
|
||||
代替の自動ドキュメントが表示されます。(<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>が提供しています。):
|
||||
|
||||

|
||||
|
||||
## アップグレード例
|
||||
|
||||
`PUT`リクエストからボディを受け取るために`main.py`を修正しましょう。
|
||||
|
||||
Pydantic によって、Python の標準的な型を使ってボディを宣言します。
|
||||
|
||||
```Python hl_lines="2 7 8 9 10 23 24 25"
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: bool = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
def update_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
サーバーは自動でリロードされます。(上述の`uvicorn`コマンドで`--reload`オプションを追加しているからです。)
|
||||
|
||||
### 自動対話型の API ドキュメントのアップグレード
|
||||
|
||||
<a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>にアクセスしましょう。
|
||||
|
||||
- 自動対話型の API ドキュメントが新しいボディも含めて自動でアップデートされます:
|
||||
|
||||

|
||||
|
||||
- "Try it out"ボタンをクリックしてください。パラメータを入力して API と直接やりとりすることができます:
|
||||
|
||||

|
||||
|
||||
- それから、"Execute" ボタンをクリックしてください。 ユーザーインターフェースは API と通信し、パラメータを送信し、結果を取得して画面に表示します:
|
||||
|
||||

|
||||
|
||||
### 代替の API ドキュメントのアップグレード
|
||||
|
||||
<a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>にアクセスしましょう。
|
||||
|
||||
- 代替の API ドキュメントにも新しいクエリパラメータやボディが反映されます。
|
||||
|
||||

|
||||
|
||||
### まとめ
|
||||
|
||||
要約すると、関数のパラメータとして、パラメータやボディ などの型を**一度だけ**宣言します。
|
||||
|
||||
標準的な最新の Python の型を使っています。
|
||||
|
||||
新しい構文や特定のライブラリのメソッドやクラスなどを覚える必要はありません。
|
||||
|
||||
単なる標準的な**3.8 以降の Python**です。
|
||||
|
||||
例えば、`int`の場合:
|
||||
|
||||
```Python
|
||||
item_id: int
|
||||
```
|
||||
|
||||
または、より複雑な`Item`モデルの場合:
|
||||
|
||||
```Python
|
||||
item: Item
|
||||
```
|
||||
|
||||
...そして、この一度の宣言で、以下のようになります:
|
||||
|
||||
- 以下を含むエディタサポート:
|
||||
- 補完
|
||||
- タイプチェック
|
||||
- データの検証:
|
||||
- データが無効な場合に自動でエラーをクリアします。
|
||||
- 深い入れ子になった JSON オブジェクトでも検証が可能です。
|
||||
- 入力データの<abbr title="also known as: serialization, parsing, marshalling">変換</abbr>: ネットワークから Python のデータや型に変換してから読み取ります:
|
||||
- JSON.
|
||||
- パスパラメータ
|
||||
- クエリパラメータ
|
||||
- クッキー
|
||||
- ヘッダー
|
||||
- フォーム
|
||||
- ファイル
|
||||
- 出力データの<abbr title="also known as: serialization, parsing, marshalling">変換</abbr>: Python のデータや型からネットワークデータへ変換します (JSON として):
|
||||
- Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
|
||||
- `datetime` オブジェクト
|
||||
- `UUID` オブジェクト
|
||||
- データベースモデル
|
||||
- ...などなど
|
||||
- 2 つの代替ユーザーインターフェースを含む自動インタラクティブ API ドキュメント:
|
||||
- Swagger UI.
|
||||
- ReDoc.
|
||||
|
||||
---
|
||||
|
||||
コード例に戻りましょう、**FastAPI** は次のようになります:
|
||||
|
||||
- `GET`および`PUT`リクエストのパスに`item_id` があることを検証します。
|
||||
- `item_id`が`GET`および`PUT`リクエストに対して`int` 型であることを検証します。
|
||||
- そうでない場合は、クライアントは有用で明確なエラーが表示されます。
|
||||
- `GET` リクエストに対してオプションのクエリパラメータ `q` (`http://127.0.0.1:8000/items/foo?q=somequery` のように) が存在するかどうかを調べます。
|
||||
- パラメータ `q` は `= None` で宣言されているので、オプションです。
|
||||
- `None`がなければ必須になります(`PUT`の場合のボディと同様です)。
|
||||
- `PUT` リクエストを `/items/{item_id}` に送信する場合は、ボディを JSON として読み込みます:
|
||||
- 必須の属性 `name` を確認してください。 それは `str` であるべきです。
|
||||
- 必須の属性 `price` を確認してください。それは `float` でなければならないです。
|
||||
- オプションの属性 `is_offer` を確認してください。値がある場合は、`bool` であるべきです。
|
||||
- これらはすべて、深くネストされた JSON オブジェクトに対しても動作します。
|
||||
- JSON から JSON に自動的に変換します。
|
||||
- OpenAPIですべてを文書化し、以下を使用することができます:
|
||||
- 対話的なドキュメントシステム。
|
||||
- 多くの言語に対応した自動クライアントコード生成システム。
|
||||
- 2 つの対話的なドキュメントのWebインターフェイスを直接提供します。
|
||||
|
||||
---
|
||||
|
||||
まだ表面的な部分に触れただけですが、もう全ての仕組みは分かっているはずです。
|
||||
|
||||
以下の行を変更してみてください:
|
||||
|
||||
```Python
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
...以下を:
|
||||
|
||||
```Python
|
||||
... "item_name": item.name ...
|
||||
```
|
||||
|
||||
...以下のように:
|
||||
|
||||
```Python
|
||||
... "item_price": item.price ...
|
||||
```
|
||||
|
||||
...そして、エディタが属性を自動補完し、そのタイプを知る方法を確認してください。:
|
||||
|
||||

|
||||
|
||||
より多くの機能を含む、より完全な例については、<a href="https://fastapi.tiangolo.com/tutorial/">チュートリアル - ユーザーガイド</a>をご覧ください。
|
||||
|
||||
**ネタバレ注意**: チュートリアル - ユーザーガイドは以下の情報が含まれています:
|
||||
|
||||
- **ヘッダー**、**クッキー**、**フォームフィールド**、**ファイル**などの他の場所からの **パラメータ** 宣言。
|
||||
- `maximum_length`や`regex`のような**検証や制約**を設定する方法。
|
||||
- 非常に強力で使いやすい <abbr title="also known as components, resources, providers, services, injectables">**依存性注入**</abbr>システム。
|
||||
- **JWT トークン**を用いた **OAuth2** や **HTTP Basic 認証** のサポートを含む、セキュリティと認証。
|
||||
- **深くネストされた JSON モデル**を宣言するためのより高度な(しかし同様に簡単な)技術(Pydantic のおかげです)。
|
||||
- 以下のようなたくさんのおまけ機能(Starlette のおかげです):
|
||||
- **WebSockets**
|
||||
- **GraphQL**
|
||||
- `httpx` や `pytest`をもとにした極限に簡単なテスト
|
||||
- **CORS**
|
||||
- **クッキーセッション**
|
||||
- ...などなど。
|
||||
|
||||
## パフォーマンス
|
||||
|
||||
独立した TechEmpower のベンチマークでは、Uvicorn で動作する**FastAPI**アプリケーションが、<a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">Python フレームワークの中で最も高速なものの 1 つ</a>であり、Starlette と Uvicorn(FastAPI で内部的に使用されています)にのみ下回っていると示されています。
|
||||
|
||||
詳細は<a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">ベンチマーク</a>セクションをご覧ください。
|
||||
|
||||
## オプションの依存関係
|
||||
|
||||
Pydantic によって使用されるもの:
|
||||
|
||||
- <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - E メールの検証
|
||||
|
||||
Starlette によって使用されるもの:
|
||||
|
||||
- <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - `TestClient`を使用するために必要です。
|
||||
- <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - デフォルトのテンプレート設定を使用する場合は必要です。
|
||||
- <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>`request.form()`からの変換をサポートしたい場合は必要です。
|
||||
- <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` サポートのためには必要です。
|
||||
- <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Starlette の `SchemaGenerator` サポートのために必要です。 (FastAPI では必要ないでしょう。)
|
||||
- <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - `GraphQLApp` サポートのためには必要です。
|
||||
|
||||
FastAPI / Starlette に使用されるもの:
|
||||
|
||||
- <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - アプリケーションをロードしてサーブするサーバーのため。
|
||||
- <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - `ORJSONResponse`を使用したい場合は必要です。
|
||||
- <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - `UJSONResponse`を使用する場合は必須です。
|
||||
|
||||
これらは全て `pip install fastapi[all]`でインストールできます。
|
||||
|
||||
## ライセンス
|
||||
|
||||
このプロジェクトは MIT ライセンスです。
|
||||
@@ -0,0 +1,5 @@
|
||||
# 学習
|
||||
|
||||
ここでは、**FastAPI** を学習するための入門セクションとチュートリアルを紹介します。
|
||||
|
||||
これは、FastAPIを学習するにあたっての**書籍**や**コース**であり、**公式**かつ推奨される方法とみなすことができます 😎
|
||||
@@ -0,0 +1,84 @@
|
||||
# プロジェクト生成 - テンプレート
|
||||
|
||||
プロジェクトジェネレーターは、初期設定、セキュリティ、データベース、初期APIエンドポイントなどの多くが含まれているため、プロジェクトの開始に利用できます。
|
||||
|
||||
プロジェクトジェネレーターは常に非常に意見が分かれる設定がされており、ニーズに合わせて更新および調整する必要があります。しかしきっと、プロジェクトの良い出発点となるでしょう。
|
||||
|
||||
## フルスタック FastAPI PostgreSQL
|
||||
|
||||
GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-postgresql</a>
|
||||
|
||||
### フルスタック FastAPI PostgreSQL - 機能
|
||||
|
||||
* 完全な**Docker**インテグレーション (Dockerベース)。
|
||||
* Docker Swarm モードデプロイ。
|
||||
* ローカル開発環境向けの**Docker Compose**インテグレーションと最適化。
|
||||
* UvicornとGunicornを使用した**リリース可能な** Python web サーバ。
|
||||
* Python <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">**FastAPI**</a> バックエンド:
|
||||
* **高速**: **NodeJS** や **Go** 並みのとても高いパフォーマンス (Starlette と Pydantic のおかげ)。
|
||||
* **直感的**: 素晴らしいエディタのサポートや <abbr title="自動補完、インテリセンスとも呼ばれる">補完。</abbr> デバッグ時間の短縮。
|
||||
* **簡単**: 簡単に利用、習得できるようなデザイン。ドキュメントを読む時間を削減。
|
||||
* **短い**: コードの重複を最小限に。パラメータ宣言による複数の機能。
|
||||
* **堅牢性**: 自動対話ドキュメントを使用した、本番環境で使用できるコード。
|
||||
* **標準規格準拠**: API のオープンスタンダードに基く、完全な互換性: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a>や <a href="http://json-schema.org/" class="external-link" target="_blank">JSON スキーマ</a>。
|
||||
* 自動バリデーション、シリアライゼーション、対話的なドキュメント、OAuth2 JWTトークンを用いた認証などを含む、<a href="https://fastapi.tiangolo.com/features/" class="external-link" target="_blank">**その他多くの機能**</a>。
|
||||
* **セキュアなパスワード** ハッシュ化 (デフォルトで)。
|
||||
* **JWTトークン** 認証。
|
||||
* **SQLAlchemy** モデル (Flask用の拡張と独立しているので、Celeryワーカーと直接的に併用できます)。
|
||||
* 基本的なユーザーモデル (任意の修正や削除が可能)。
|
||||
* **Alembic** マイグレーション。
|
||||
* **CORS** (Cross Origin Resource Sharing (オリジン間リソース共有))。
|
||||
* **Celery** ワーカー。バックエンドの残りの部分からモデルとコードを選択的にインポートし、使用可能。
|
||||
* Dockerと統合された**Pytest**ベースのRESTバックエンドテスト。データベースに依存せずに、全てのAPIをテスト可能。Docker上で動作するので、毎回ゼロから新たなデータストアを構築可能。(ElasticSearch、MongoDB、CouchDBなどを使用して、APIの動作をテスト可能)
|
||||
* Atom HydrogenやVisual Studio Code Jupyterなどの拡張機能を使用した、リモートまたはDocker開発用の**Jupyterカーネル**との簡単なPython統合。
|
||||
* **Vue** フロントエンド:
|
||||
* Vue CLIにより生成。
|
||||
* **JWT認証**の処理。
|
||||
* ログインビュー。
|
||||
* ログイン後の、メインダッシュボードビュー。
|
||||
* メインダッシュボードでのユーザー作成と編集。
|
||||
* セルフユーザー版
|
||||
* **Vuex**。
|
||||
* **Vue-router**。
|
||||
* 美しいマテリアルデザインコンポーネントのための**Vuetify**。
|
||||
* **TypeScript**。
|
||||
* **Nginx**ベースのDockerサーバ (Vue-routerとうまく協調する構成)。
|
||||
* Dockerマルチステージビルド。コンパイルされたコードの保存やコミットが不要。
|
||||
* ビルド時にフロントエンドテスト実行 (無効化も可能)。
|
||||
* 可能な限りモジュール化されているのでそのまま使用できますが、Vue CLIで再生成したり、必要に応じて作成したりして、必要なものを再利用可能。
|
||||
* PostgreSQLデータベースのための**PGAdmin**。(PHPMyAdminとMySQLを使用できるように簡単に変更可能)
|
||||
* Celeryジョブ監視のための**Flower**。
|
||||
* **Traefik**を使用してフロントエンドとバックエンド間をロードバランシング。同一ドメインに配置しパスで区切る、ただし、異なるコンテナで処理。
|
||||
* Traefik統合。Let's Encrypt **HTTPS**証明書の自動生成を含む。
|
||||
* GitLab **CI** (継続的インテグレーション)。フロントエンドおよびバックエンドテストを含む。
|
||||
|
||||
## フルスタック FastAPI Couchbase
|
||||
|
||||
GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-couchbase</a>
|
||||
|
||||
⚠️ **警告** ⚠️
|
||||
|
||||
ゼロから新規プロジェクトを始める場合は、ここで代替案を確認してください。
|
||||
|
||||
例えば、<a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">フルスタック FastAPI PostgreSQL</a>のプロジェクトジェネレーターは、積極的にメンテナンスされ、利用されているのでより良い代替案かもしれません。また、すべての新機能と改善点が含まれています。
|
||||
|
||||
Couchbaseベースのジェネレーターは今も無償提供されています。恐らく正常に動作するでしょう。また、すでにそのジェネレーターで生成されたプロジェクトが存在する場合でも (ニーズに合わせてアップデートしているかもしれません)、同様に正常に動作するはずです。
|
||||
|
||||
詳細はレポジトリのドキュメントを参照して下さい。
|
||||
|
||||
## フルスタック FastAPI MongoDB
|
||||
|
||||
...時間の都合等によっては、今後作成されるかもしれません。😅 🎉
|
||||
|
||||
## spaCyとFastAPIを使用した機械学習モデル
|
||||
|
||||
GitHub: <a href="https://github.com/microsoft/cookiecutter-spacy-fastapi" class="external-link" target="_blank">https://github.com/microsoft/cookiecutter-spacy-fastapi</a>
|
||||
|
||||
### spaCyとFastAPIを使用した機械学習モデル - 機能
|
||||
|
||||
* **spaCy** のNERモデルの統合。
|
||||
* **Azure Cognitive Search** のリクエストフォーマットを搭載。
|
||||
* **リリース可能な** UvicornとGunicornを使用したPythonウェブサーバ。
|
||||
* **Azure DevOps** のKubernetes (AKS) CI/CD デプロイを搭載。
|
||||
* **多言語** プロジェクトのために、セットアップ時に言語を容易に選択可能 (spaCyに組み込まれている言語の中から)。
|
||||
* **簡単に拡張可能**。spaCyだけでなく、他のモデルフレームワーク (Pytorch、Tensorflow) へ。
|
||||
@@ -0,0 +1,314 @@
|
||||
# Pythonの型の紹介
|
||||
|
||||
**Python 3.6以降** では「型ヒント」オプションがサポートされています。
|
||||
|
||||
これらの **"型ヒント"** は変数の<abbr title="例: str, int, float, bool">型</abbr>を宣言することができる新しい構文です。(Python 3.6以降)
|
||||
|
||||
変数に型を宣言することでエディターやツールがより良いサポートを提供することができます。
|
||||
|
||||
ここではPythonの型ヒントについての **クイックチュートリアル/リフレッシュ** で、**FastAPI**でそれらを使用するために必要な最低限のことだけをカバーしています。...実際には本当に少ないです。
|
||||
|
||||
**FastAPI** はすべてこれらの型ヒントに基づいており、多くの強みと利点を与えてくれます。
|
||||
|
||||
しかしたとえまったく **FastAPI** を使用しない場合でも、それらについて少し学ぶことで利点を得ることができるでしょう。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
もしあなたがPythonの専門家で、すでに型ヒントについてすべて知っているのであれば、次の章まで読み飛ばしてください。
|
||||
|
||||
///
|
||||
|
||||
## 動機
|
||||
|
||||
簡単な例から始めてみましょう:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001.py *}
|
||||
|
||||
|
||||
このプログラムを実行すると以下が出力されます:
|
||||
|
||||
```
|
||||
John Doe
|
||||
```
|
||||
|
||||
この関数は以下のようなことを行います:
|
||||
|
||||
* `first_name`と`last_name`を取得します。
|
||||
* `title()`を用いて、それぞれの最初の文字を大文字に変換します。
|
||||
* 真ん中にスペースを入れて<abbr title="次から次へと中身を入れて一つにまとめる">連結</abbr>します。
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001.py hl[2] *}
|
||||
|
||||
|
||||
### 編集
|
||||
|
||||
これはとても簡単なプログラムです。
|
||||
|
||||
しかし、今、あなたがそれを一から書いていたと想像してみてください。
|
||||
|
||||
パラメータの準備ができていたら、そのとき、関数の定義を始めていたことでしょう...
|
||||
|
||||
しかし、そうすると「最初の文字を大文字に変換するあのメソッド」を呼び出す必要があります。
|
||||
|
||||
それは`upper`でしたか?`uppercase`でしたか?それとも`first_uppercase`?または`capitalize`?
|
||||
|
||||
そして、古くからプログラマーの友人であるエディタで自動補完を試してみます。
|
||||
|
||||
関数の最初のパラメータ`first_name`を入力し、ドット(`.`)を入力してから、`Ctrl+Space`を押すと補完が実行されます。
|
||||
|
||||
しかし、悲しいことに、これはなんの役にも立ちません:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/python-types/image01.png">
|
||||
|
||||
### 型の追加
|
||||
|
||||
先ほどのコードから一行変更してみましょう。
|
||||
|
||||
以下の関数のパラメータ部分を:
|
||||
|
||||
```Python
|
||||
first_name, last_name
|
||||
```
|
||||
|
||||
以下へ変更します:
|
||||
|
||||
```Python
|
||||
first_name: str, last_name: str
|
||||
```
|
||||
|
||||
これだけです。
|
||||
|
||||
それが「型ヒント」です:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial002.py hl[1] *}
|
||||
|
||||
|
||||
これは、以下のようにデフォルト値を宣言するのと同じではありません:
|
||||
|
||||
```Python
|
||||
first_name="john", last_name="doe"
|
||||
```
|
||||
|
||||
それとは別物です。
|
||||
|
||||
イコール(`=`)ではなく、コロン(`:`)を使用します。
|
||||
|
||||
そして、通常、型ヒントを追加しても、それらがない状態と起こることは何も変わりません。
|
||||
|
||||
しかし今、あなたが再びその関数を作成している最中に、型ヒントを使っていると想像してみて下さい。
|
||||
|
||||
同じタイミングで`Ctrl+Space`で自動補完を実行すると、以下のようになります:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/python-types/image02.png">
|
||||
|
||||
これであれば、あなたは「ベルを鳴らす」一つを見つけるまで、オプションを見て、スクロールすることができます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/python-types/image03.png">
|
||||
|
||||
## より強い動機
|
||||
|
||||
この関数を見てください。すでに型ヒントを持っています:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial003.py hl[1] *}
|
||||
|
||||
|
||||
エディタは変数の型を知っているので、補完だけでなく、エラーチェックをすることもできます。
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/python-types/image04.png">
|
||||
|
||||
これで`age`を`str(age)`で文字列に変換して修正する必要があることがわかります:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial004.py hl[2] *}
|
||||
|
||||
|
||||
## 型の宣言
|
||||
|
||||
関数のパラメータとして、型ヒントを宣言している主な場所を確認しました。
|
||||
|
||||
これは **FastAPI** で使用する主な場所でもあります。
|
||||
|
||||
### 単純な型
|
||||
|
||||
`str`だけでなく、Pythonの標準的な型すべてを宣言することができます。
|
||||
|
||||
例えば、以下を使用可能です:
|
||||
|
||||
* `int`
|
||||
* `float`
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
{* ../../docs_src/python_types/tutorial005.py hl[1] *}
|
||||
|
||||
|
||||
### 型パラメータを持つジェネリック型
|
||||
|
||||
データ構造の中には、`dict`、`list`、`set`、そして`tuple`のように他の値を含むことができるものがあります。また内部の値も独自の型を持つことができます。
|
||||
|
||||
これらの型や内部の型を宣言するには、Pythonの標準モジュール`typing`を使用します。
|
||||
|
||||
これらの型ヒントをサポートするために特別に存在しています。
|
||||
|
||||
#### `List`
|
||||
|
||||
例えば、`str`の`list`の変数を定義してみましょう。
|
||||
|
||||
`typing`から`List`をインポートします(大文字の`L`を含む):
|
||||
|
||||
{* ../../docs_src/python_types/tutorial006.py hl[1] *}
|
||||
|
||||
|
||||
同じようにコロン(`:`)の構文で変数を宣言します。
|
||||
|
||||
型として、`List`を入力します。
|
||||
|
||||
リストはいくつかの内部の型を含む型なので、それらを角括弧で囲んでいます。
|
||||
|
||||
{* ../../docs_src/python_types/tutorial006.py hl[4] *}
|
||||
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
角括弧内の内部の型は「型パラメータ」と呼ばれています。
|
||||
|
||||
この場合、`str`は`List`に渡される型パラメータです。
|
||||
|
||||
///
|
||||
|
||||
つまり: 変数`items`は`list`であり、このリストの各項目は`str`です。
|
||||
|
||||
そうすることで、エディタはリストの項目を処理している間にもサポートを提供できます。
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/python-types/image05.png">
|
||||
|
||||
タイプがなければ、それはほぼ不可能です。
|
||||
|
||||
変数`item`はリスト`items`の要素の一つであることに注意してください。
|
||||
|
||||
それでも、エディタはそれが`str`であることを知っていて、そのためのサポートを提供しています。
|
||||
|
||||
#### `Tuple` と `Set`
|
||||
|
||||
`tuple`と`set`の宣言も同様です:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial007.py hl[1,4] *}
|
||||
|
||||
|
||||
つまり:
|
||||
|
||||
* 変数`items_t`は`int`、`int`、`str`の3つの項目を持つ`tuple`です
|
||||
|
||||
* 変数`items_s`はそれぞれの項目が`bytes`型である`set`です。
|
||||
|
||||
#### `Dict`
|
||||
|
||||
`dict`を宣言するためには、カンマ区切りで2つの型パラメータを渡します。
|
||||
|
||||
最初の型パラメータは`dict`のキーです。
|
||||
|
||||
2番目の型パラメータは`dict`の値です。
|
||||
|
||||
{* ../../docs_src/python_types/tutorial008.py hl[1,4] *}
|
||||
|
||||
|
||||
つまり:
|
||||
|
||||
* 変数`prices`は`dict`であり:
|
||||
* この`dict`のキーは`str`型です。(つまり、各項目の名前)
|
||||
* この`dict`の値は`float`型です。(つまり、各項目の価格)
|
||||
|
||||
#### `Optional`
|
||||
|
||||
また、`Optional`を使用して、変数が`str`のような型を持つことを宣言することもできますが、それは「オプション」であり、`None`にすることもできます。
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
ただの`str`の代わりに`Optional[str]`を使用することで、エディタは値が常に`str`であると仮定している場合に実際には`None`である可能性があるエラーを検出するのに役立ちます。
|
||||
|
||||
#### ジェネリック型
|
||||
|
||||
以下のように角括弧で型パラメータを取る型を:
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Optional`
|
||||
* ...など
|
||||
|
||||
**ジェネリック型** または **ジェネリクス** と呼びます。
|
||||
|
||||
### 型としてのクラス
|
||||
|
||||
変数の型としてクラスを宣言することもできます。
|
||||
|
||||
例えば、`Person`クラスという名前のクラスがあるとしましょう:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010.py hl[1,2,3] *}
|
||||
|
||||
|
||||
変数の型を`Person`として宣言することができます:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010.py hl[6] *}
|
||||
|
||||
|
||||
そして、再び、すべてのエディタのサポートを得ることができます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/python-types/image06.png">
|
||||
|
||||
## Pydanticのモデル
|
||||
|
||||
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> はデータ検証を行うためのPythonライブラリです。
|
||||
|
||||
データの「形」を属性付きのクラスとして宣言します。
|
||||
|
||||
そして、それぞれの属性は型を持ちます。
|
||||
|
||||
さらに、いくつかの値を持つクラスのインスタンスを作成すると、その値を検証し、適切な型に変換して(もしそうであれば)全てのデータを持つオブジェクトを提供してくれます。
|
||||
|
||||
また、その結果のオブジェクトですべてのエディタのサポートを受けることができます。
|
||||
|
||||
Pydanticの公式ドキュメントから引用:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial011.py *}
|
||||
|
||||
|
||||
/// info | 情報
|
||||
|
||||
Pydanticについてより学びたい方は<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">ドキュメントを参照してください</a>.
|
||||
|
||||
///
|
||||
|
||||
**FastAPI** はすべてPydanticをベースにしています。
|
||||
|
||||
すべてのことは[チュートリアル - ユーザーガイド](tutorial/index.md){.internal-link target=_blank}で実際に見ることができます。
|
||||
|
||||
## **FastAPI**での型ヒント
|
||||
|
||||
**FastAPI** はこれらの型ヒントを利用していくつかのことを行います。
|
||||
|
||||
**FastAPI** では型ヒントを使って型パラメータを宣言すると以下のものが得られます:
|
||||
|
||||
* **エディタサポート**.
|
||||
* **型チェック**.
|
||||
|
||||
...そして **FastAPI** は同じように宣言をすると、以下のことを行います:
|
||||
|
||||
* **要件の定義**: リクエストパスパラメータ、クエリパラメータ、ヘッダー、ボディ、依存関係などから要件を定義します。
|
||||
* **データの変換**: リクエストのデータを必要な型に変換します。
|
||||
* **データの検証**: リクエストごとに:
|
||||
* データが無効な場合にクライアントに返される **自動エラー** を生成します。
|
||||
* **ドキュメント** OpenAPIを使用したAPI:
|
||||
* 自動的に対話型ドキュメントのユーザーインターフェイスで使用されます。
|
||||
|
||||
すべてが抽象的に聞こえるかもしれません。心配しないでください。 この全ての動作は [チュートリアル - ユーザーガイド](tutorial/index.md){.internal-link target=_blank}で見ることができます。
|
||||
|
||||
重要なのは、Pythonの標準的な型を使うことで、(クラスやデコレータなどを追加するのではなく)1つの場所で **FastAPI** が多くの作業を代わりにやってくれているということです。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
すでにすべてのチュートリアルを終えて、型についての詳細を見るためにこのページに戻ってきた場合は、<a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">`mypy`のチートシートを参照してください</a>
|
||||
|
||||
///
|
||||
@@ -0,0 +1,84 @@
|
||||
# バックグラウンドタスク
|
||||
|
||||
レスポンスを返した *後に* 実行されるバックグラウンドタスクを定義できます。
|
||||
|
||||
これは、リクエスト後に処理を開始する必要があるが、クライアントがレスポンスを受け取る前に処理を終える必要のない操作に役立ちます。
|
||||
|
||||
これには、たとえば次のものが含まれます。
|
||||
|
||||
* 作業実行後のメール通知:
|
||||
* メールサーバーへの接続とメールの送信は「遅い」(数秒) 傾向があるため、すぐにレスポンスを返し、バックグラウンドでメール通知ができます。
|
||||
* データ処理:
|
||||
* たとえば、時間のかかる処理を必要とするファイル受信時には、「受信済み」(HTTP 202) のレスポンスを返し、バックグラウンドで処理できます。
|
||||
|
||||
## `BackgroundTasks` の使用
|
||||
|
||||
まず初めに、`BackgroundTasks` をインポートし、` BackgroundTasks` の型宣言と共に、*path operation 関数* のパラメーターを定義します:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
|
||||
|
||||
**FastAPI** は、`BackgroundTasks` 型のオブジェクトを作成し、そのパラメーターに渡します。
|
||||
|
||||
## タスク関数の作成
|
||||
|
||||
バックグラウンドタスクとして実行される関数を作成します。
|
||||
|
||||
これは、パラメーターを受け取ることができる単なる標準的な関数です。
|
||||
|
||||
これは `async def` または通常の `def` 関数であり、**FastAPI** はこれを正しく処理します。
|
||||
|
||||
ここで、タスク関数はファイル書き込みを実行します (メール送信のシミュレーション)。
|
||||
|
||||
また、書き込み操作では `async` と `await` を使用しないため、通常の `def` で関数を定義します。
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
|
||||
|
||||
## バックグラウンドタスクの追加
|
||||
|
||||
*path operations 関数* 内で、`.add_task()` メソッドを使用してタスク関数を *background tasks* オブジェクトに渡します。
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
|
||||
|
||||
`.add_task()` は以下の引数を受け取ります:
|
||||
|
||||
* バックグラウンドで実行されるタスク関数 (`write_notification`)。
|
||||
* タスク関数に順番に渡す必要のある引数の列 (`email`)。
|
||||
* タスク関数に渡す必要のあるキーワード引数 (`message="some notification"`)。
|
||||
|
||||
## 依存性注入
|
||||
|
||||
`BackgroundTasks` の使用は依存性注入システムでも機能し、様々な階層 (*path operations 関数*、依存性 (依存可能性)、サブ依存性など) で `BackgroundTasks` 型のパラメーターを宣言できます。
|
||||
|
||||
**FastAPI** は、それぞれの場合の処理方法と同じオブジェクトの再利用方法を知っているため、すべてのバックグラウンドタスクがマージされ、バックグラウンドで後で実行されます。
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *}
|
||||
|
||||
この例では、レスポンスが送信された *後* にメッセージが `log.txt` ファイルに書き込まれます。
|
||||
|
||||
リクエストにクエリがあった場合、バックグラウンドタスクでログに書き込まれます。
|
||||
|
||||
そして、*path operations 関数* で生成された別のバックグラウンドタスクは、`email` パスパラメータを使用してメッセージを書き込みます。
|
||||
|
||||
## 技術的な詳細
|
||||
|
||||
`BackgroundTasks` クラスは、<a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`starlette.background`</a>から直接取得されます。
|
||||
|
||||
これは、FastAPI に直接インポート/インクルードされるため、`fastapi` からインポートできる上に、`starlette.background`から別の `BackgroundTask` (末尾に `s` がない) を誤ってインポートすることを回避できます。
|
||||
|
||||
`BackgroundTasks`のみを使用することで (`BackgroundTask` ではなく)、`Request` オブジェクトを直接使用する場合と同様に、それを *path operations 関数* パラメーターとして使用し、**FastAPI** に残りの処理を任せることができます。
|
||||
|
||||
それでも、FastAPI で `BackgroundTask` を単独で使用することは可能ですが、コード内でオブジェクトを作成し、それを含むStarlette `Response` を返す必要があります。
|
||||
|
||||
詳細については、<a href="https://www.starlette.dev/background/" class="external-link" target="_blank">バックグラウンドタスクに関する Starlette の公式ドキュメント</a>を参照して下さい。
|
||||
|
||||
## 警告
|
||||
|
||||
大量のバックグラウンド計算が必要であり、必ずしも同じプロセスで実行する必要がない場合 (たとえば、メモリや変数などを共有する必要がない場合)、<a href="https://www.celeryproject.org/" class="external-link" target="_blank">Celery</a> のようなより大きな他のツールを使用するとメリットがあるかもしれません。
|
||||
|
||||
これらは、より複雑な構成、RabbitMQ や Redis などのメッセージ/ジョブキューマネージャーを必要とする傾向がありますが、複数のプロセス、特に複数のサーバーでバックグラウンドタスクを実行できます。
|
||||
|
||||
ただし、同じ **FastAPI** アプリから変数とオブジェクトにアクセスする必要がある場合、または小さなバックグラウンドタスク (電子メール通知の送信など) を実行する必要がある場合は、単に `BackgroundTasks` を使用できます。
|
||||
|
||||
## まとめ
|
||||
|
||||
`BackgroundTasks` をインポートして、*path operations 関数* や依存関係のパラメータに `BackgroundTasks`を使用し、バックグラウンドタスクを追加して下さい。
|
||||
@@ -0,0 +1,53 @@
|
||||
# ボディ - フィールド
|
||||
|
||||
`Query`や`Path`、`Body`を使って *path operation関数* のパラメータに追加のバリデーションやメタデータを宣言するのと同じように、Pydanticの`Field`を使ってPydanticモデルの内部でバリデーションやメタデータを宣言することができます。
|
||||
|
||||
## `Field`のインポート
|
||||
|
||||
まず、以下のようにインポートします:
|
||||
|
||||
{* ../../docs_src/body_fields/tutorial001.py hl[4] *}
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
`Field`は他の全てのもの(`Query`、`Path`、`Body`など)とは違い、`fastapi`からではなく、`pydantic`から直接インポートされていることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
## モデルの属性の宣言
|
||||
|
||||
以下のように`Field`をモデルの属性として使用することができます:
|
||||
|
||||
{* ../../docs_src/body_fields/tutorial001.py hl[11,12,13,14] *}
|
||||
|
||||
`Field`は`Query`や`Path`、`Body`と同じように動作し、全く同様のパラメータなどを持ちます。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
実際には次に見る`Query`や`Path`などは、共通の`Param`クラスのサブクラスのオブジェクトを作成しますが、それ自体はPydanticの`FieldInfo`クラスのサブクラスです。
|
||||
|
||||
また、Pydanticの`Field`は`FieldInfo`のインスタンスも返します。
|
||||
|
||||
`Body`は`FieldInfo`のサブクラスのオブジェクトを直接返すこともできます。そして、他にも`Body`クラスのサブクラスであるものがあります。
|
||||
|
||||
`fastapi`から`Query`や`Path`などをインポートする場合、これらは実際には特殊なクラスを返す関数であることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
型、デフォルト値、`Field`を持つ各モデルの属性が、`Path`や`Query`、`Body`の代わりに`Field`を持つ、*path operation 関数の*パラメータと同じ構造になっていることに注目してください。
|
||||
|
||||
///
|
||||
|
||||
## 追加情報の追加
|
||||
|
||||
追加情報は`Field`や`Query`、`Body`などで宣言することができます。そしてそれは生成されたJSONスキーマに含まれます。
|
||||
|
||||
後に例を用いて宣言を学ぶ際に、追加情報を追加する方法を学べます。
|
||||
|
||||
## まとめ
|
||||
|
||||
Pydanticの`Field`を使用して、モデルの属性に追加のバリデーションやメタデータを宣言することができます。
|
||||
|
||||
追加のキーワード引数を使用して、追加のJSONスキーマのメタデータを渡すこともできます。
|
||||
@@ -0,0 +1,167 @@
|
||||
# ボディ - 複数のパラメータ
|
||||
|
||||
これまで`Path`と`Query`をどう使うかを見てきましたが、リクエストボディの宣言のより高度な使い方を見てみましょう。
|
||||
|
||||
## `Path`、`Query`とボディパラメータを混ぜる
|
||||
|
||||
まず、もちろん、`Path`と`Query`とリクエストボディのパラメータの宣言は自由に混ぜることができ、 **FastAPI** は何をするべきかを知っています。
|
||||
|
||||
また、デフォルトの`None`を設定することで、ボディパラメータをオプションとして宣言することもできます:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial001.py hl[19,20,21] *}
|
||||
|
||||
/// note | 備考
|
||||
|
||||
この場合、ボディから取得する`item`はオプションであることに注意してください。デフォルト値は`None`です。
|
||||
|
||||
///
|
||||
|
||||
## 複数のボディパラメータ
|
||||
|
||||
上述の例では、*path operations*は`item`の属性を持つ以下のようなJSONボディを期待していました:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
}
|
||||
```
|
||||
|
||||
しかし、`item`と`user`のように複数のボディパラメータを宣言することもできます:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial002.py hl[22] *}
|
||||
|
||||
この場合、**FastAPI**は関数内に複数のボディパラメータ(Pydanticモデルである2つのパラメータ)があることに気付きます。
|
||||
|
||||
そのため、パラメータ名をボディのキー(フィールド名)として使用し、以下のようなボディを期待しています:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item": {
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
},
|
||||
"user": {
|
||||
"username": "dave",
|
||||
"full_name": "Dave Grohl"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
/// note | 備考
|
||||
|
||||
以前と同じように`item`が宣言されていたにもかかわらず、`item`はキー`item`を持つボディの内部にあることが期待されていることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
**FastAPI** はリクエストから自動で変換を行い、パラメータ`item`が特定の内容を受け取り、`user`も同じように特定の内容を受け取ります。
|
||||
|
||||
複合データの検証を行い、OpenAPIスキーマや自動ドキュメントのように文書化してくれます。
|
||||
|
||||
## ボディ内の単数値
|
||||
|
||||
クエリとパスパラメータの追加データを定義するための `Query` と `Path` があるのと同じように、 **FastAPI** は同等の `Body` を提供します。
|
||||
|
||||
例えば、前のモデルを拡張して、同じボディに `item` と `user` の他にもう一つのキー `importance` を入れたいと決めることができます。
|
||||
|
||||
単数値なのでそのまま宣言すると、**FastAPI** はそれがクエリパラメータであるとみなします。
|
||||
|
||||
しかし、`Body`を使用して、**FastAPI** に別のボディキーとして扱うように指示することができます:
|
||||
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial003.py hl[23] *}
|
||||
|
||||
この場合、**FastAPI** は以下のようなボディを期待します:
|
||||
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item": {
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
},
|
||||
"user": {
|
||||
"username": "dave",
|
||||
"full_name": "Dave Grohl"
|
||||
},
|
||||
"importance": 5
|
||||
}
|
||||
```
|
||||
|
||||
繰り返しになりますが、データ型の変換、検証、文書化などを行います。
|
||||
|
||||
## 複数のボディパラメータとクエリ
|
||||
|
||||
もちろん、ボディパラメータに加えて、必要に応じて追加のクエリパラメータを宣言することもできます。
|
||||
|
||||
デフォルトでは、単数値はクエリパラメータとして解釈されるので、明示的に `Query` を追加する必要はありません。
|
||||
|
||||
```Python
|
||||
q: str = None
|
||||
```
|
||||
|
||||
以下において:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial004.py hl[27] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
`Body`もまた、後述する `Query` や `Path` などと同様に、すべての検証パラメータとメタデータパラメータを持っています。
|
||||
|
||||
///
|
||||
|
||||
## 単一のボディパラメータの埋め込み
|
||||
|
||||
Pydanticモデル`Item`のボディパラメータ`item`を1つだけ持っているとしましょう。
|
||||
|
||||
デフォルトでは、**FastAPI**はそのボディを直接期待します。
|
||||
|
||||
しかし、追加のボディパラメータを宣言したときのように、キー `item` を持つ JSON とその中のモデルの内容を期待したい場合は、特別な `Body` パラメータ `embed` を使うことができます:
|
||||
|
||||
```Python
|
||||
item: Item = Body(..., embed=True)
|
||||
```
|
||||
|
||||
以下において:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial005.py hl[17] *}
|
||||
|
||||
この場合、**FastAPI** は以下のようなボディを期待します:
|
||||
|
||||
```JSON hl_lines="2"
|
||||
{
|
||||
"item": {
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
以下の代わりに:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
}
|
||||
```
|
||||
|
||||
## まとめ
|
||||
|
||||
リクエストが単一のボディしか持てない場合でも、*path operation関数*に複数のボディパラメータを追加することができます。
|
||||
|
||||
しかし、**FastAPI** はそれを処理し、関数内の正しいデータを与え、*path operation*内の正しいスキーマを検証し、文書化します。
|
||||
|
||||
また、ボディの一部として受け取る単数値を宣言することもできます。
|
||||
|
||||
また、単一のパラメータしか宣言されていない場合でも、ボディをキーに埋め込むように **FastAPI** に指示することができます。
|
||||
@@ -0,0 +1,231 @@
|
||||
# ボディ - ネストされたモデル
|
||||
|
||||
**FastAPI** を使用すると、深くネストされた任意のモデルを定義、検証、文書化、使用することができます(Pydanticのおかげです)。
|
||||
|
||||
## リストのフィールド
|
||||
|
||||
属性をサブタイプとして定義することができます。例えば、Pythonの`list`は以下のように定義できます:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial001.py hl[12] *}
|
||||
|
||||
これにより、各項目の型は宣言されていませんが、`tags`はある項目のリストになります。
|
||||
|
||||
## タイプパラメータを持つリストのフィールド
|
||||
|
||||
しかし、Pythonには型や「タイプパラメータ」を使ってリストを宣言する方法があります:
|
||||
|
||||
### typingの`List`をインポート
|
||||
|
||||
まず、Pythonの標準の`typing`モジュールから`List`をインポートします:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
|
||||
|
||||
### タイプパラメータを持つ`List`の宣言
|
||||
|
||||
`list`や`dict`、`tuple`のようなタイプパラメータ(内部の型)を持つ型を宣言するには:
|
||||
|
||||
* `typing`モジュールからそれらをインストールします。
|
||||
* 角括弧(`[`と`]`)を使って「タイプパラメータ」として内部の型を渡します:
|
||||
|
||||
```Python
|
||||
from typing import List
|
||||
|
||||
my_list: List[str]
|
||||
```
|
||||
|
||||
型宣言の標準的なPythonの構文はこれだけです。
|
||||
|
||||
内部の型を持つモデルの属性にも同じ標準の構文を使用してください。
|
||||
|
||||
そのため、以下の例では`tags`を具体的な「文字列のリスト」にすることができます:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial002.py hl[14] *}
|
||||
|
||||
## セット型
|
||||
|
||||
しかし、よく考えてみると、タグは繰り返すべきではなく、おそらくユニークな文字列になるのではないかと気付いたとします。
|
||||
|
||||
そして、Pythonにはユニークな項目のセットのための特別なデータ型`set`があります。
|
||||
|
||||
そのため、以下のように、`Set`をインポートして`str`の`set`として`tags`を宣言することができます:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial003.py hl[1,14] *}
|
||||
|
||||
これを使えば、データが重複しているリクエストを受けた場合でも、ユニークな項目のセットに変換されます。
|
||||
|
||||
そして、そのデータを出力すると、たとえソースに重複があったとしても、固有の項目のセットとして出力されます。
|
||||
|
||||
また、それに応じて注釈をつけたり、文書化したりします。
|
||||
|
||||
## ネストされたモデル
|
||||
|
||||
Pydanticモデルの各属性には型があります。
|
||||
|
||||
しかし、その型はそれ自体が別のPydanticモデルである可能性があります。
|
||||
|
||||
そのため、特定の属性名、型、バリデーションを指定して、深くネストしたJSON`object`を宣言することができます。
|
||||
|
||||
すべては、任意のネストにされています。
|
||||
|
||||
### サブモデルの定義
|
||||
|
||||
例えば、`Image`モデルを定義することができます:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial004.py hl[9,10,11] *}
|
||||
|
||||
### サブモデルを型として使用
|
||||
|
||||
そして、それを属性の型として使用することができます:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial004.py hl[20] *}
|
||||
|
||||
これは **FastAPI** が以下のようなボディを期待することを意味します:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2,
|
||||
"tags": ["rock", "metal", "bar"],
|
||||
"image": {
|
||||
"url": "http://example.com/baz.jpg",
|
||||
"name": "The Foo live"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
繰り返しになりますが、**FastAPI** を使用して、その宣言を行うだけで以下のような恩恵を受けられます:
|
||||
|
||||
* ネストされたモデルでも対応可能なエディタのサポート(補完など)
|
||||
* データ変換
|
||||
* データの検証
|
||||
* 自動文書化
|
||||
|
||||
## 特殊な型とバリデーション
|
||||
|
||||
`str`や`int`、`float`のような通常の単数型の他にも、`str`を継承したより複雑な単数型を使うこともできます。
|
||||
|
||||
すべてのオプションをみるには、<a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">Pydanticのエキゾチック な型</a>のドキュメントを確認してください。次の章でいくつかの例をみることができます。
|
||||
|
||||
例えば、`Image`モデルのように`url`フィールドがある場合、`str`の代わりにPydanticの`HttpUrl`を指定することができます:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial005.py hl[4,10] *}
|
||||
|
||||
文字列は有効なURLであることが確認され、そのようにJSONスキーマ・OpenAPIで文書化されます。
|
||||
|
||||
## サブモデルのリストを持つ属性
|
||||
|
||||
Pydanticモデルを`list`や`set`などのサブタイプとして使用することもできます:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial006.py hl[20] *}
|
||||
|
||||
これは、次のようなJSONボディを期待します(変換、検証、ドキュメントなど):
|
||||
|
||||
```JSON hl_lines="11"
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2,
|
||||
"tags": [
|
||||
"rock",
|
||||
"metal",
|
||||
"bar"
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"url": "http://example.com/baz.jpg",
|
||||
"name": "The Foo live"
|
||||
},
|
||||
{
|
||||
"url": "http://example.com/dave.jpg",
|
||||
"name": "The Baz"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
/// info | 情報
|
||||
|
||||
`images`キーが画像オブジェクトのリストを持つようになったことに注目してください。
|
||||
|
||||
///
|
||||
|
||||
## 深くネストされたモデル
|
||||
|
||||
深くネストされた任意のモデルを定義することができます:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial007.py hl[9,14,20,23,27] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
`Offer`は`Item`のリストであり、オプションの`Image`のリストを持っていることに注目してください。
|
||||
|
||||
///
|
||||
|
||||
## 純粋なリストのボディ
|
||||
|
||||
期待するJSONボディのトップレベルの値がJSON`array`(Pythonの`list`)であれば、Pydanticモデルと同じように、関数のパラメータで型を宣言することができます:
|
||||
|
||||
```Python
|
||||
images: List[Image]
|
||||
```
|
||||
|
||||
以下のように:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial008.py hl[15] *}
|
||||
|
||||
## あらゆる場所でのエディタサポート
|
||||
|
||||
エディタのサポートもどこでも受けることができます。
|
||||
|
||||
以下のようにリストの中の項目でも:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/body-nested-models/image01.png">
|
||||
|
||||
Pydanticモデルではなく、`dict`を直接使用している場合はこのようなエディタのサポートは得られません。
|
||||
|
||||
しかし、それらについて心配する必要はありません。入力された辞書は自動的に変換され、出力も自動的にJSONに変換されます。
|
||||
|
||||
## 任意の`dict`のボディ
|
||||
|
||||
また、ある型のキーと別の型の値を持つ`dict`としてボディを宣言することもできます。
|
||||
|
||||
有効なフィールド・属性名を事前に知る必要がありません(Pydanticモデルの場合のように)。
|
||||
|
||||
これは、まだ知らないキーを受け取りたいときに便利だと思います。
|
||||
|
||||
---
|
||||
|
||||
他にも、`int`のように他の型のキーを持ちたい場合などに便利です。
|
||||
|
||||
それをここで見ていきましょう。
|
||||
|
||||
この場合、`int`のキーと`float`の値を持つものであれば、どんな`dict`でも受け入れることができます:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial009.py hl[15] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
JSONはキーとして`str`しかサポートしていないことに注意してください。
|
||||
|
||||
しかしPydanticには自動データ変換機能があります。
|
||||
|
||||
これは、APIクライアントがキーとして文字列しか送信できなくても、それらの文字列に純粋な整数が含まれている限り、Pydanticが変換して検証することを意味します。
|
||||
|
||||
そして、`weights`として受け取る`dict`は、実際には`int`のキーと`float`の値を持つことになります。
|
||||
|
||||
///
|
||||
|
||||
## まとめ
|
||||
|
||||
**FastAPI** を使用すると、Pydanticモデルが提供する最大限の柔軟性を持ちながら、コードをシンプルに短く、エレガントに保つことができます。
|
||||
|
||||
以下のような利点があります:
|
||||
|
||||
* エディタのサポート(どこでも補完!)
|
||||
* データ変換(別名:構文解析・シリアライズ)
|
||||
* データの検証
|
||||
* スキーマ文書
|
||||
* 自動文書化
|
||||
@@ -0,0 +1,100 @@
|
||||
# ボディ - 更新
|
||||
|
||||
## `PUT`による置換での更新
|
||||
|
||||
項目を更新するには<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTPの`PUT`</a>操作を使用することができます。
|
||||
|
||||
`jsonable_encoder`を用いて、入力データをJSON形式で保存できるデータに変換することができます(例:NoSQLデータベース)。例えば、`datetime`を`str`に変換します。
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial001.py hl[30,31,32,33,34,35] *}
|
||||
|
||||
既存のデータを置き換えるべきデータを受け取るために`PUT`は使用されます。
|
||||
|
||||
### 置換についての注意
|
||||
|
||||
つまり、`PUT`を使用して以下のボディで項目`bar`を更新したい場合は:
|
||||
|
||||
```Python
|
||||
{
|
||||
"name": "Barz",
|
||||
"price": 3,
|
||||
"description": None,
|
||||
}
|
||||
```
|
||||
|
||||
すでに格納されている属性`"tax": 20.2`を含まないため、入力モデルのデフォルト値は`"tax": 10.5`です。
|
||||
|
||||
そして、データはその「新しい」`10.5`の`tax`と共に保存されます。
|
||||
|
||||
## `PATCH`による部分的な更新
|
||||
|
||||
また、<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTPの`PATCH`</a>操作でデータを*部分的に*更新することもできます。
|
||||
|
||||
つまり、更新したいデータだけを送信して、残りはそのままにしておくことができます。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
`PATCH`は`PUT`よりもあまり使われておらず、知られていません。
|
||||
|
||||
また、多くのチームは部分的な更新であっても`PUT`だけを使用しています。
|
||||
|
||||
**FastAPI** はどんな制限も課けていないので、それらを使うのは **自由** です。
|
||||
|
||||
しかし、このガイドでは、それらがどのように使用されることを意図しているかを多かれ少なかれ、示しています。
|
||||
|
||||
///
|
||||
|
||||
### Pydanticの`exclude_unset`パラメータの使用
|
||||
|
||||
部分的な更新を受け取りたい場合は、Pydanticモデルの`.dict()`の`exclude_unset`パラメータを使用すると非常に便利です。
|
||||
|
||||
`item.dict(exclude_unset=True)`のように。
|
||||
|
||||
これにより、`item`モデルの作成時に設定されたデータのみを持つ`dict`が生成され、デフォルト値は除外されます。
|
||||
|
||||
これを使うことで、デフォルト値を省略して、設定された(リクエストで送られた)データのみを含む`dict`を生成することができます:
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002.py hl[34] *}
|
||||
|
||||
### Pydanticの`update`パラメータ
|
||||
|
||||
ここで、`.copy()`を用いて既存のモデルのコピーを作成し、`update`パラメータに更新するデータを含む`dict`を渡すことができます。
|
||||
|
||||
`stored_item_model.copy(update=update_data)`のように:
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002.py hl[35] *}
|
||||
|
||||
### 部分的更新のまとめ
|
||||
|
||||
まとめると、部分的な更新を適用するには、次のようにします:
|
||||
|
||||
* (オプションで)`PUT`の代わりに`PATCH`を使用します。
|
||||
* 保存されているデータを取得します。
|
||||
* そのデータをPydanticモデルにいれます。
|
||||
* 入力モデルからデフォルト値を含まない`dict`を生成します(`exclude_unset`を使用します)。
|
||||
* この方法では、モデル内のデフォルト値ですでに保存されている値を上書きするのではなく、ユーザーが実際に設定した値のみを更新することができます。
|
||||
* 保存されているモデルのコピーを作成し、受け取った部分的な更新で属性を更新します(`update`パラメータを使用します)。
|
||||
* コピーしたモデルをDBに保存できるものに変換します(例えば、`jsonable_encoder`を使用します)。
|
||||
* これはモデルの`.dict()`メソッドを再度利用することに匹敵しますが、値をJSONに変換できるデータ型、例えば`datetime`を`str`に変換します。
|
||||
* データをDBに保存します。
|
||||
* 更新されたモデルを返します。
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002.py hl[30,31,32,33,34,35,36,37] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
実際には、HTTPの`PUT`操作でも同じテクニックを使用することができます。
|
||||
|
||||
しかし、これらのユースケースのために作成されたので、ここでの例では`PATCH`を使用しています。
|
||||
|
||||
///
|
||||
|
||||
/// note | 備考
|
||||
|
||||
入力モデルがまだ検証されていることに注目してください。
|
||||
|
||||
そのため、すべての属性を省略できる部分的な変更を受け取りたい場合は、すべての属性をオプションとしてマークしたモデルを用意する必要があります(デフォルト値または`None`を使用して)。
|
||||
|
||||
**更新** のためのオプション値がすべて設定されているモデルと、**作成** のための必須値が設定されているモデルを区別するには、[追加モデル](extra-models.md){.internal-link target=_blank}で説明されている考え方を利用することができます。
|
||||
|
||||
///
|
||||
@@ -0,0 +1,162 @@
|
||||
# リクエストボディ
|
||||
|
||||
クライアント (ブラウザなど) からAPIにデータを送信する必要があるとき、データを **リクエストボディ (request body)** として送ります。
|
||||
|
||||
**リクエスト** ボディはクライアントによってAPIへ送られます。**レスポンス** ボディはAPIがクライアントに送るデータです。
|
||||
|
||||
APIはほとんどの場合 **レスポンス** ボディを送らなければなりません。しかし、クライアントは必ずしも **リクエスト** ボディを送らなければいけないわけではありません。
|
||||
|
||||
**リクエスト** ボディを宣言するために <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> モデルを使用します。そして、その全てのパワーとメリットを利用します。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
データを送るには、`POST` (もっともよく使われる)、`PUT`、`DELETE` または `PATCH` を使うべきです。
|
||||
|
||||
GET リクエストでボディを送信することは、仕様では未定義の動作ですが、FastAPI でサポートされており、非常に複雑な(極端な)ユースケースにのみ対応しています。
|
||||
|
||||
非推奨なので、Swagger UIを使った対話型のドキュメントにはGETのボディ情報は表示されません。さらに、中継するプロキシが対応していない可能性があります。
|
||||
|
||||
///
|
||||
|
||||
## Pydanticの `BaseModel` をインポート
|
||||
|
||||
ます初めに、 `pydantic` から `BaseModel` をインポートする必要があります:
|
||||
|
||||
{* ../../docs_src/body/tutorial001.py hl[4] *}
|
||||
|
||||
## データモデルの作成
|
||||
|
||||
そして、`BaseModel` を継承したクラスとしてデータモデルを宣言します。
|
||||
|
||||
すべての属性にpython標準の型を使用します:
|
||||
|
||||
{* ../../docs_src/body/tutorial001.py hl[7:11] *}
|
||||
|
||||
クエリパラメータの宣言と同様に、モデル属性がデフォルト値をもつとき、必須な属性ではなくなります。それ以外は必須になります。オプショナルな属性にしたい場合は `None` を使用してください。
|
||||
|
||||
例えば、上記のモデルは以下の様なJSON「`オブジェクト`」(もしくはPythonの `dict` ) を宣言しています:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "An optional description",
|
||||
"price": 45.2,
|
||||
"tax": 3.5
|
||||
}
|
||||
```
|
||||
|
||||
...`description` と `tax` はオプショナル (デフォルト値は `None`) なので、以下のJSON「`オブジェクト`」も有効です:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"price": 45.2
|
||||
}
|
||||
```
|
||||
|
||||
## パラメータとして宣言
|
||||
|
||||
*パスオペレーション* に加えるために、パスパラメータやクエリパラメータと同じ様に宣言します:
|
||||
|
||||
{* ../../docs_src/body/tutorial001.py hl[18] *}
|
||||
|
||||
...そして、作成したモデル `Item` で型を宣言します。
|
||||
|
||||
## 結果
|
||||
|
||||
そのPythonの型宣言だけで **FastAPI** は以下のことを行います:
|
||||
|
||||
* リクエストボディをJSONとして読み取ります。
|
||||
* 適当な型に変換します(必要な場合)。
|
||||
* データを検証します。
|
||||
* データが無効な場合は、明確なエラーが返され、どこが不正なデータであったかを示します。
|
||||
* 受け取ったデータをパラメータ `item` に変換します。
|
||||
* 関数内で `Item` 型であると宣言したので、すべての属性とその型に対するエディタサポート(補完など)をすべて使用できます。
|
||||
* モデルの<a href="http://json-schema.org" class="external-link" target="_blank">JSONスキーマ</a>定義を生成し、好きな場所で使用することができます。
|
||||
* これらのスキーマは、生成されたOpenAPIスキーマの一部となり、自動ドキュメントの<abbr title = "User Interfaces">UI</abbr>に使用されます。
|
||||
|
||||
## 自動ドキュメント生成
|
||||
|
||||
モデルのJSONスキーマはOpenAPIで生成されたスキーマの一部になり、対話的なAPIドキュメントに表示されます:
|
||||
|
||||
<img src="/img/tutorial/body/image01.png">
|
||||
|
||||
そして、それらが使われる *パスオペレーション* のそれぞれのAPIドキュメントにも表示されます:
|
||||
|
||||
<img src="/img/tutorial/body/image02.png">
|
||||
|
||||
## エディターサポート
|
||||
|
||||
エディターによる型ヒントと補完が関数内で利用できます (Pydanticモデルではなく `dict` を受け取ると、同じサポートは受けられません):
|
||||
|
||||
<img src="/img/tutorial/body/image03.png">
|
||||
|
||||
型によるエラーチェックも可能です:
|
||||
|
||||
<img src="/img/tutorial/body/image04.png">
|
||||
|
||||
これは偶然ではなく、このデザインに基づいてフレームワークが作られています。
|
||||
|
||||
全てのエディターで機能することを確認するために、実装前の設計時に徹底的にテストしました。
|
||||
|
||||
これをサポートするためにPydantic自体にもいくつかの変更がありました。
|
||||
|
||||
上記のスクリーンショットは<a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>を撮ったものです。
|
||||
|
||||
しかし、<a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>やほとんどのPythonエディタでも同様なエディターサポートを受けられます:
|
||||
|
||||
<img src="/img/tutorial/body/image05.png">
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
<a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>エディタを使用している場合は、<a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>が使用可能です。
|
||||
|
||||
以下のエディターサポートが強化されます:
|
||||
|
||||
* 自動補完
|
||||
* 型チェック
|
||||
* リファクタリング
|
||||
* 検索
|
||||
* インスペクション
|
||||
|
||||
///
|
||||
|
||||
## モデルの使用
|
||||
|
||||
関数内部で、モデルの全ての属性に直接アクセスできます:
|
||||
|
||||
{* ../../docs_src/body/tutorial002.py hl[21] *}
|
||||
|
||||
## リクエストボディ + パスパラメータ
|
||||
|
||||
パスパラメータとリクエストボディを同時に宣言できます。
|
||||
|
||||
**FastAPI** はパスパラメータである関数パラメータは**パスから受け取り**、Pydanticモデルによって宣言された関数パラメータは**リクエストボディから受け取る**ということを認識します。
|
||||
|
||||
{* ../../docs_src/body/tutorial003.py hl[17:18] *}
|
||||
|
||||
## リクエストボディ + パスパラメータ + クエリパラメータ
|
||||
|
||||
また、**ボディ**と**パス**と**クエリ**のパラメータも同時に宣言できます。
|
||||
|
||||
**FastAPI** はそれぞれを認識し、適切な場所からデータを取得します。
|
||||
|
||||
{* ../../docs_src/body/tutorial004.py hl[18] *}
|
||||
|
||||
関数パラメータは以下の様に認識されます:
|
||||
|
||||
* パラメータが**パス**で宣言されている場合は、優先的にパスパラメータとして扱われます。
|
||||
* パラメータが**単数型** (`int`、`float`、`str`、`bool` など)の場合は**クエリ**パラメータとして解釈されます。
|
||||
* パラメータが **Pydantic モデル**型で宣言された場合、リクエスト**ボディ**として解釈されます。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
FastAPIは、`= None`があるおかげで、`q`がオプショナルだとわかります。
|
||||
|
||||
`Optional[str]` の`Optional` はFastAPIでは使用されていません(FastAPIは`str`の部分のみ使用します)。しかし、`Optional[str]` はエディタがコードのエラーを見つけるのを助けてくれます。
|
||||
|
||||
///
|
||||
|
||||
## Pydanticを使わない方法
|
||||
|
||||
もしPydanticモデルを使用したくない場合は、**Body**パラメータが利用できます。[Body - Multiple Parameters: Singular values in body](body-multiple-params.md#_2){.internal-link target=_blank}を確認してください。
|
||||
@@ -0,0 +1,77 @@
|
||||
# クッキーパラメータモデル
|
||||
|
||||
もし関連する**複数のクッキー**から成るグループがあるなら、それらを宣言するために、**Pydanticモデル**を作成できます。🍪
|
||||
|
||||
こうすることで、**複数の場所**で**そのPydanticモデルを再利用**でき、バリデーションやメタデータを、すべてのクッキーパラメータに対して一度に宣言できます。😎
|
||||
|
||||
/// note | 備考
|
||||
|
||||
この機能は、FastAPIのバージョン `0.115.0` からサポートされています。🤓
|
||||
|
||||
///
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
これと同じテクニックは `Query` 、 `Cookie` 、 `Header` にも適用できます。 😎
|
||||
|
||||
///
|
||||
|
||||
## クッキーにPydanticモデルを使用する
|
||||
|
||||
必要な複数の**クッキー**パラメータを**Pydanticモデル**で宣言し、さらに、それを `Cookie` として宣言しましょう:
|
||||
|
||||
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
|
||||
|
||||
**FastAPI**は、リクエストの**クッキー**から**それぞれのフィールド**のデータを**抽出**し、定義された**Pydanticモデル**を提供します。
|
||||
|
||||
## ドキュメントの確認
|
||||
|
||||
対話的APIドキュメントUI `/docs` で、定義されているクッキーを確認できます:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/cookie-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
/// info | 備考
|
||||
|
||||
|
||||
**ブラウザがクッキーを処理し**ていますが、特別な方法で内部的に処理を行っているために、**JavaScript**からは簡単に操作**できない**ことに留意してください。
|
||||
|
||||
**対話的APIドキュメントUI** `/docs` にアクセスすれば、*パスオペレーション*に関するクッキーの**ドキュメンテーション**を確認できます。
|
||||
|
||||
しかし、たとえ**クッキーデータを入力して**「Execute」をクリックしても、対話的APIドキュメントUIは**JavaScript**で動作しているためクッキーは送信されず、まるで値を入力しなかったかのような**エラー**メッセージが表示されます。
|
||||
|
||||
///
|
||||
|
||||
## 余分なクッキーを禁止する
|
||||
|
||||
特定の(あまり一般的ではないかもしれない)ケースで、受け付けるクッキーを**制限**する必要があるかもしれません。
|
||||
|
||||
あなたのAPIは独自の <abbr title="念のためですが、これはジョークです。クッキー同意とは関係ありませんが、APIでさえ不適切なクッキーを拒否できるとは愉快ですね。クッキーでも食べてください。🍪 (原文: This is a joke, just in case. It has nothing to do with cookie consents, but it's funny that even the API can now reject the poor cookies. Have a cookie. 🍪)">クッキー同意</abbr> を管理する能力を持っています。 🤪🍪
|
||||
|
||||
Pydanticのモデルの Configuration を利用して、 `extra` フィールドを `forbid` とすることができます。
|
||||
|
||||
{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *}
|
||||
|
||||
もしクライアントが**余分なクッキー**を送ろうとすると、**エラー**レスポンスが返されます。
|
||||
|
||||
<abbr title="これもジョークです。気にしないでください。クッキーのお供にコーヒーでも飲んでください。☕ (原文: This is another joke. Don't pay attention to me. Have some coffee for your cookie. ☕)">どうせAPIに拒否されるのに</abbr>あなたの同意を得ようと精一杯努力する可哀想なクッキーバナーたち... 🍪
|
||||
|
||||
例えば、クライアントがクッキー `santa_tracker` を `good-list-please` という値で送ろうとすると、`santa_tracker` という <abbr title="サンタはクッキー不足を良しとはしないでしょう。🎅 はい、クッキージョークはもう止めておきます。(原文: Santa disapproves the lack of cookies. 🎅 Okay, no more cookie jokes.)">クッキーが許可されていない</abbr> ことを通知する**エラー**レスポンスが返されます:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["cookie", "santa_tracker"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "good-list-please",
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## まとめ
|
||||
|
||||
**FastAPI**では、<abbr title="帰ってしまう前に最後のクッキーをどうぞ。🍪 (原文: Have a last cookie before you go. 🍪)">**クッキー**</abbr>を宣言するために、**Pydanticモデル**を使用できます。😎
|
||||
@@ -0,0 +1,35 @@
|
||||
# クッキーのパラメータ
|
||||
|
||||
クッキーのパラメータは、`Query`や`Path`のパラメータを定義するのと同じ方法で定義できます。
|
||||
|
||||
## `Cookie`をインポート
|
||||
|
||||
まず、`Cookie`をインポートします:
|
||||
|
||||
{* ../../docs_src/cookie_params/tutorial001.py hl[3] *}
|
||||
|
||||
## `Cookie`のパラメータを宣言
|
||||
|
||||
次に、`Path`や`Query`と同じ構造を使ってクッキーのパラメータを宣言します。
|
||||
|
||||
最初の値がデフォルト値で、追加の検証パラメータや注釈パラメータをすべて渡すことができます:
|
||||
|
||||
{* ../../docs_src/cookie_params/tutorial001.py hl[9] *}
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`Cookie`は`Path`と`Query`の「姉妹」クラスです。また、同じ共通の`Param`クラスを継承しています。
|
||||
|
||||
しかし、`fastapi`から`Query`や`Path`、`Cookie`などをインポートする場合、それらは実際には特殊なクラスを返す関数であることを覚えておいてください。
|
||||
|
||||
///
|
||||
|
||||
/// info | 情報
|
||||
|
||||
クッキーを宣言するには、`Cookie`を使う必要があります。なぜなら、そうしないとパラメータがクエリのパラメータとして解釈されてしまうからです。
|
||||
|
||||
///
|
||||
|
||||
## まとめ
|
||||
|
||||
クッキーは`Cookie`を使って宣言し、`Query`や`Path`と同じパターンを使用する。
|
||||
@@ -0,0 +1,85 @@
|
||||
# CORS (オリジン間リソース共有)
|
||||
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">CORSまたは「オリジン間リソース共有」</a> は、ブラウザで実行されているフロントエンドにバックエンドと通信するJavaScriptコードがあり、そのバックエンドがフロントエンドとは異なる「オリジン」にある状況を指します。
|
||||
|
||||
## オリジン
|
||||
|
||||
オリジンはプロトコル (`http`、`https`) とドメイン (`myapp.com`、`localhost`、`localhost.tiangolo.com`) とポート (`80`、`443`、`8080`) の組み合わせです。
|
||||
|
||||
したがって、以下はすべて異なるオリジンです:
|
||||
|
||||
* `http://localhost`
|
||||
* `https://localhost`
|
||||
* `http://localhost:8080`
|
||||
|
||||
すべて `localhost` であっても、異なるプロトコルやポートを使用するので、異なる「オリジン」です。
|
||||
|
||||
## ステップ
|
||||
|
||||
そして、ブラウザ上で実行されているフロントエンド (`http://localhost:8080`) があり、そのJavaScriptが `http://localhost` で実行されているバックエンドと通信するとします。(ポートを指定していないので、ブラウザはデフォルトの`80`ポートを使用します)
|
||||
|
||||
次に、ブラウザはHTTPの `OPTIONS` リクエストをバックエンドに送信します。そして、バックエンドがこの異なるオリジン (`http://localhost:8080`) からの通信を許可する適切なヘッダーを送信すると、ブラウザはフロントエンドのJavaScriptにバックエンドへのリクエストを送信させます。
|
||||
|
||||
これを実現するには、バックエンドに「許可されたオリジン」のリストがなければなりません。
|
||||
|
||||
この場合、フロントエンドを正しく機能させるには、そのリストに `http://localhost:8080` を含める必要があります。
|
||||
|
||||
## ワイルドカード
|
||||
|
||||
リストを `"*"` (ワイルドカード) と宣言して、すべてを許可することもできます。
|
||||
|
||||
ただし、Bearer Tokenで使用されるような認証ヘッダーやCookieなどのクレデンシャル情報に関するものを除いて、特定の種類の通信のみが許可されます。
|
||||
|
||||
したがって、すべてを正しく機能させるために、許可されたオリジンの明示的な指定をお勧めします。
|
||||
|
||||
## `CORSMiddleware` の使用
|
||||
|
||||
**FastAPI** アプリケーションでは `CORSMiddleware` を使用して、CORSに関する設定ができます。
|
||||
|
||||
* `CORSMiddleware`をインポートします。
|
||||
* 許可されたオリジンのリストを (文字列として) 作成します。
|
||||
* これを「ミドルウェア」として **FastAPI** アプリケーションに追加します。
|
||||
|
||||
以下も、バックエンドに許可させるかどうか指定できます:
|
||||
|
||||
* クレデンシャル情報 (認証ヘッダー、Cookieなど) 。
|
||||
* 特定のHTTPメソッド (`POST`、`PUT`) またはワイルドカード `"*"` を使用してすべて許可。
|
||||
* 特定のHTTPヘッダー、またはワイルドカード `"*"`を使用してすべて許可。
|
||||
|
||||
{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
|
||||
|
||||
`CORSMiddleware` 実装のデフォルトのパラメータはCORSに関して制限を与えるものになっているので、ブラウザにドメインを跨いで特定のオリジン、メソッド、またはヘッダーを使用可能にするためには、それらを明示的に有効にする必要があります
|
||||
|
||||
以下の引数がサポートされています:
|
||||
|
||||
* `allow_origins` - オリジン間リクエストを許可するオリジンのリスト。例えば、`['https://example.org', 'https://www.example.org']`。`['*']`を使用して任意のオリジンを許可できます。
|
||||
* `allow_origin_regex` - オリジン間リクエストを許可するオリジンの正規表現文字列。例えば、`'https://.*\.example\.org'`。
|
||||
* `allow_methods` - オリジン間リクエストで許可するHTTPメソッドのリスト。デフォルトは `['GET']` です。`['*']`を使用してすべての標準メソッドを許可できます。
|
||||
* `allow_headers` - オリジン間リクエストでサポートするHTTPリクエストヘッダーのリスト。デフォルトは `[]` です。`['*']`を使用して、すべてのヘッダーを許可できます。CORSリクエストでは、 `Accept` 、 `Accept-Language` 、 `Content-Language` 、 `Content-Type` ヘッダーが常に許可されます。
|
||||
* `allow_credentials` - オリジン間リクエストでCookieをサポートする必要があることを示します。デフォルトは `False` です。
|
||||
* `expose_headers` - ブラウザからアクセスできるようにするレスポンスヘッダーを示します。デフォルトは `[]` です。
|
||||
* `max_age` - ブラウザがCORSレスポンスをキャッシュする最大時間を秒単位で設定します。デフォルトは `600` です。
|
||||
|
||||
このミドルウェアは2種類のHTTPリクエストに応答します...
|
||||
|
||||
### CORSプリフライトリクエスト
|
||||
|
||||
これらは、 `Origin` ヘッダーと `Access-Control-Request-Method` ヘッダーを持つ `OPTIONS` リクエストです。
|
||||
|
||||
この場合、ミドルウェアはリクエストを横取りし、適切なCORSヘッダーと共に情報提供のために `200` または `400` のレスポンスを返します。
|
||||
|
||||
### シンプルなリクエスト
|
||||
|
||||
`Origin` ヘッダーのあるリクエスト。この場合、ミドルウェアは通常どおりリクエストに何もしないですが、レスポンスに適切なCORSヘッダーを加えます。
|
||||
|
||||
## より詳しい情報
|
||||
|
||||
<abbr title="Cross-Origin Resource Sharing (オリジン間リソース共有)">CORS</abbr>についてより詳しい情報は、<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">Mozilla CORS documentation</a> を参照して下さい。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`from starlette.middleware.cors import CORSMiddleware` も使用できます。
|
||||
|
||||
**FastAPI** は、開発者の利便性を高めるために、`fastapi.middleware` でいくつかのミドルウェアを提供します。利用可能なミドルウェアのほとんどは、Starletteから直接提供されています。
|
||||
|
||||
///
|
||||
@@ -0,0 +1,113 @@
|
||||
# デバッグ
|
||||
|
||||
Visual Studio CodeやPyCharmなどを使用して、エディター上でデバッガーと連携できます。
|
||||
|
||||
## `uvicorn` の実行
|
||||
|
||||
FastAPIアプリケーション上で、`uvicorn` を直接インポートして実行します:
|
||||
|
||||
{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
|
||||
|
||||
### `__name__ == "__main__"` について
|
||||
|
||||
`__name__ == "__main__"` の主な目的は、ファイルが次のコマンドで呼び出されたときに実行されるコードを用意することです:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python myapp.py
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
ただし、次のように、別のファイルからインポートされるときには呼び出されません:
|
||||
|
||||
```Python
|
||||
from myapp import app
|
||||
```
|
||||
|
||||
#### より詳しい説明
|
||||
|
||||
ファイルの名前が `myapp.py` だとします。
|
||||
|
||||
以下の様に実行する場合:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python myapp.py
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Pythonによって自動的に作成されたファイル内の内部変数 `__name__` は、値として文字列 `"__main__"` を持ちます。
|
||||
|
||||
なので、以下:
|
||||
|
||||
```Python
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
は実行されます。
|
||||
|
||||
---
|
||||
|
||||
そのモジュール (ファイル) をインポートした場合は、こうはなりません。
|
||||
|
||||
したがって、次のようなもう一つのファイル `importer.py` がある場合:
|
||||
|
||||
```Python
|
||||
from myapp import app
|
||||
|
||||
# Some more code
|
||||
```
|
||||
|
||||
`myapp.py` 内の自動変数には、値が `"__main __"` の変数 `__name__` はありません。
|
||||
|
||||
したがって、以下の行:
|
||||
|
||||
```Python
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
は実行されません。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
より詳しい情報は、<a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">公式Pythonドキュメント</a>を参照してください。
|
||||
|
||||
///
|
||||
|
||||
## デバッガーでコードを実行
|
||||
|
||||
コードから直接Uvicornサーバーを実行しているため、デバッガーから直接Pythonプログラム (FastAPIアプリケーション) を呼び出せます。
|
||||
|
||||
---
|
||||
|
||||
例えば、Visual Studio Codeでは、次のことが可能です:
|
||||
|
||||
* 「デバッグ」パネルに移動。
|
||||
* 「構成の追加...」
|
||||
* 「Python」を選択。
|
||||
* オプション「`Python: Current File (Integrated Terminal)`」を指定してデバッガーを実行。
|
||||
|
||||
すると、**FastAPI** コードでサーバーが起動され、ブレークポイントで停止したりするでしょう。
|
||||
|
||||
以下の様な画面になります:
|
||||
|
||||
<img src="/img/tutorial/debugging/image01.png">
|
||||
|
||||
---
|
||||
|
||||
Pycharmを使用する場合、次のことが可能です:
|
||||
|
||||
* 「実行」メニューをオープン。
|
||||
* オプション「デバッグ...」を選択。
|
||||
* 次にコンテキストメニューが表示される。
|
||||
* デバッグするファイル (ここでは `main.py`) を選択。
|
||||
|
||||
すると、**FastAPI** コードでサーバーが起動され、ブレークポイントで停止したりするでしょう。
|
||||
|
||||
以下の様な画面になります:
|
||||
|
||||
<img src="/img/tutorial/debugging/image02.png">
|
||||
@@ -0,0 +1,180 @@
|
||||
# 依存関係としてのクラス
|
||||
|
||||
**依存性注入** システムを深く掘り下げる前に、先ほどの例をアップグレードしてみましょう。
|
||||
|
||||
## 前の例の`dict`
|
||||
|
||||
前の例では、依存関係("dependable")から`dict`を返していました:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial001.py hl[9] *}
|
||||
|
||||
しかし、*path operation関数*のパラメータ`commons`に`dict`が含まれています。
|
||||
|
||||
また、エディタは`dict`のキーと値の型を知ることができないため、多くのサポート(補完のような)を提供することができません。
|
||||
|
||||
もっとうまくやれるはずです...。
|
||||
|
||||
## 依存関係を作るもの
|
||||
|
||||
これまでは、依存関係が関数として宣言されているのを見てきました。
|
||||
|
||||
しかし、依存関係を定義する方法はそれだけではありません(その方が一般的かもしれませんが)。
|
||||
|
||||
重要なのは、依存関係が「呼び出し可能」なものであることです。
|
||||
|
||||
Pythonにおける「**呼び出し可能**」とは、Pythonが関数のように「呼び出す」ことができるものを指します。
|
||||
|
||||
そのため、`something`オブジェクト(関数ではないかもしれませんが)を持っていて、それを次のように「呼び出す」(実行する)ことができるとします:
|
||||
|
||||
```Python
|
||||
something()
|
||||
```
|
||||
|
||||
または
|
||||
|
||||
```Python
|
||||
something(some_argument, some_keyword_argument="foo")
|
||||
```
|
||||
|
||||
これを「呼び出し可能」なものと呼びます。
|
||||
|
||||
## 依存関係としてのクラス
|
||||
|
||||
Pythonのクラスのインスタンスを作成する際に、同じ構文を使用していることに気づくかもしれません。
|
||||
|
||||
例えば:
|
||||
|
||||
```Python
|
||||
class Cat:
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
|
||||
|
||||
fluffy = Cat(name="Mr Fluffy")
|
||||
```
|
||||
|
||||
この場合、`fluffy`は`Cat`クラスのインスタンスです。
|
||||
|
||||
そして`fluffy`を作成するために、`Cat`を「呼び出している」ことになります。
|
||||
|
||||
そのため、Pythonのクラスもまた「呼び出し可能」です。
|
||||
|
||||
そして、**FastAPI** では、Pythonのクラスを依存関係として使用することができます。
|
||||
|
||||
FastAPIが実際にチェックしているのは、それが「呼び出し可能」(関数、クラス、その他なんでも)であり、パラメータが定義されているかどうかということです。
|
||||
|
||||
**FastAPI** の依存関係として「呼び出し可能なもの」を渡すと、その「呼び出し可能なもの」のパラメータを解析し、サブ依存関係も含めて、*path operation関数*のパラメータと同じように処理します。
|
||||
|
||||
それは、パラメータが全くない呼び出し可能なものにも適用されます。パラメータのない*path operation関数*と同じように。
|
||||
|
||||
そこで、上で紹介した依存関係の`common_parameters`を`CommonQueryParams`クラスに変更します:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial002.py hl[11,12,13,14,15] *}
|
||||
|
||||
クラスのインスタンスを作成するために使用される`__init__`メソッドに注目してください:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial002.py hl[12] *}
|
||||
|
||||
...以前の`common_parameters`と同じパラメータを持っています:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial001.py hl[8] *}
|
||||
|
||||
これらのパラメータは **FastAPI** が依存関係を「解決」するために使用するものです。
|
||||
|
||||
どちらの場合も以下を持っています:
|
||||
|
||||
* オプショナルの`q`クエリパラメータ。
|
||||
* `skip`クエリパラメータ、デフォルトは`0`。
|
||||
* `limit`クエリパラメータ、デフォルトは`100`。
|
||||
|
||||
どちらの場合も、データは変換され、検証され、OpenAPIスキーマなどで文書化されます。
|
||||
|
||||
## 使用
|
||||
|
||||
これで、このクラスを使用して依存関係を宣言することができます。
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial002.py hl[19] *}
|
||||
|
||||
**FastAPI** は`CommonQueryParams`クラスを呼び出します。これにより、そのクラスの「インスタンス」が作成され、インスタンスはパラメータ`commons`として関数に渡されます。
|
||||
|
||||
## 型注釈と`Depends`
|
||||
|
||||
上のコードでは`CommonQueryParams`を2回書いていることに注目してください:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
以下にある最後の`CommonQueryParams`:
|
||||
|
||||
```Python
|
||||
... = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
...は、**FastAPI** が依存関係を知るために実際に使用するものです。
|
||||
|
||||
そこからFastAPIが宣言されたパラメータを抽出し、それが実際にFastAPIが呼び出すものです。
|
||||
|
||||
---
|
||||
|
||||
この場合、以下にある最初の`CommonQueryParams`:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams ...
|
||||
```
|
||||
|
||||
...は **FastAPI** に対して特別な意味をもちません。FastAPIはデータ変換や検証などには使用しません(それらのためには`= Depends(CommonQueryParams)`を使用しています)。
|
||||
|
||||
実際には以下のように書けばいいだけです:
|
||||
|
||||
```Python
|
||||
commons = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
以下にあるように:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial003.py hl[19] *}
|
||||
|
||||
しかし、型を宣言することは推奨されています。そうすれば、エディタは`commons`のパラメータとして何が渡されるかを知ることができ、コードの補完や型チェックなどを行うのに役立ちます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/dependencies/image02.png">
|
||||
|
||||
## ショートカット
|
||||
|
||||
しかし、ここでは`CommonQueryParams`を2回書くというコードの繰り返しが発生していることがわかります:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
依存関係が、クラス自体のインスタンスを作成するために**FastAPI**が「呼び出す」*特定の*クラスである場合、**FastAPI** はこれらのケースのショートカットを提供しています。
|
||||
|
||||
それらの具体的なケースについては以下のようにします:
|
||||
|
||||
以下のように書く代わりに:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
...以下のように書きます:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends()
|
||||
```
|
||||
|
||||
パラメータの型として依存関係を宣言し、`Depends()`の中でパラメータを指定せず、`Depends()`をその関数のパラメータの「デフォルト」値(`=`のあとの値)として使用することで、`Depends(CommonQueryParams)`の中でクラス全体を*もう一度*書かなくてもよくなります。
|
||||
|
||||
同じ例では以下のようになります:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial004.py hl[19] *}
|
||||
|
||||
...そして **FastAPI** は何をすべきか知っています。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
役に立つというよりも、混乱するようであれば無視してください。それをする*必要*はありません。
|
||||
|
||||
それは単なるショートカットです。なぜなら **FastAPI** はコードの繰り返しを最小限に抑えることに気を使っているからです。
|
||||
|
||||
///
|
||||
@@ -0,0 +1,57 @@
|
||||
# path operationデコレータの依存関係
|
||||
|
||||
場合によっては*path operation関数*の中で依存関係の戻り値を本当に必要としないこともあります。
|
||||
|
||||
もしくは、依存関係が値を返さない場合もあります。
|
||||
|
||||
しかし、それでも実行・解決する必要があります。
|
||||
|
||||
このような場合、*path operation関数*のパラメータを`Depends`で宣言する代わりに、*path operation decorator*に`dependencies`の`list`を追加することができます。
|
||||
|
||||
## *path operationデコレータ*への`dependencies`の追加
|
||||
|
||||
*path operationデコレータ*はオプショナルの引数`dependencies`を受け取ります。
|
||||
|
||||
それは`Depends()`の`list`であるべきです:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006.py hl[17] *}
|
||||
|
||||
これらの依存関係は、通常の依存関係と同様に実行・解決されます。しかし、それらの値(何かを返す場合)は*path operation関数*には渡されません。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
エディタによっては、未使用の関数パラメータをチェックしてエラーとして表示するものもあります。
|
||||
|
||||
`dependencies`を`path operationデコレータ`で使用することで、エディタやツールのエラーを回避しながら確実に実行することができます。
|
||||
|
||||
また、コードの未使用のパラメータがあるのを見て、それが不要だと思ってしまうような新しい開発者の混乱を避けるのにも役立つかもしれません。
|
||||
|
||||
///
|
||||
|
||||
## 依存関係のエラーと戻り値
|
||||
|
||||
通常使用している依存関係の*関数*と同じものを使用することができます。
|
||||
|
||||
### 依存関係の要件
|
||||
|
||||
これらはリクエストの要件(ヘッダのようなもの)やその他のサブ依存関係を宣言することができます:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006.py hl[6,11] *}
|
||||
|
||||
### 例外の発生
|
||||
|
||||
これらの依存関係は通常の依存関係と同じように、例外を`raise`発生させることができます:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006.py hl[8,13] *}
|
||||
|
||||
### 戻り値
|
||||
|
||||
そして、値を返すことも返さないこともできますが、値は使われません。
|
||||
|
||||
つまり、すでにどこかで使っている通常の依存関係(値を返すもの)を再利用することができ、値は使われなくても依存関係は実行されます:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006.py hl[9,14] *}
|
||||
|
||||
## *path operations*のグループに対する依存関係
|
||||
|
||||
後で、より大きなアプリケーションの構造([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank})について読む時に、おそらく複数のファイルを使用して、*path operations*のグループに対して単一の`dependencies`パラメータを宣言する方法を学ぶでしょう。
|
||||
@@ -0,0 +1,241 @@
|
||||
# yieldを持つ依存関係
|
||||
|
||||
FastAPIは、いくつかの<abbr title='時々"exit"、"cleanup"、"teardown"、"close"、"context managers"、 ...のように呼ばれる'>終了後の追加のステップ</abbr>を行う依存関係をサポートしています。
|
||||
|
||||
これを行うには、`return`の代わりに`yield`を使い、その後に追加のステップを書きます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`yield`は必ず一度だけ使用するようにしてください。
|
||||
|
||||
///
|
||||
|
||||
/// info | 情報
|
||||
|
||||
これを動作させるには、**Python 3.7** 以上を使用するか、**Python 3.6** では"backports"をインストールする必要があります:
|
||||
|
||||
```
|
||||
pip install async-exit-stack async-generator
|
||||
```
|
||||
|
||||
これにより<a href="https://github.com/sorcio/async_exit_stack" class="external-link" target="_blank">async-exit-stack</a>と<a href="https://github.com/python-trio/async_generator" class="external-link" target="_blank">async-generator</a>がインストールされます。
|
||||
|
||||
///
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
以下と一緒に使用できる関数なら何でも有効です:
|
||||
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a>または
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
|
||||
|
||||
これらは **FastAPI** の依存関係として使用するのに有効です。
|
||||
|
||||
実際、FastAPIは内部的にこれら2つのデコレータを使用しています。
|
||||
|
||||
///
|
||||
|
||||
## `yield`を持つデータベースの依存関係
|
||||
|
||||
例えば、これを使ってデータベースセッションを作成し、終了後にそれを閉じることができます。
|
||||
|
||||
レスポンスを送信する前に`yield`文を含む前のコードのみが実行されます。
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[2,3,4] *}
|
||||
|
||||
生成された値は、*path operations*や他の依存関係に注入されるものです:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
|
||||
|
||||
`yield`文に続くコードは、レスポンスが送信された後に実行されます:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[5,6] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`async`や通常の関数を使用することができます。
|
||||
|
||||
**FastAPI** は、通常の依存関係と同じように、それぞれで正しいことを行います。
|
||||
|
||||
///
|
||||
|
||||
## `yield`と`try`を持つ依存関係
|
||||
|
||||
`yield`を持つ依存関係で`try`ブロックを使用した場合、その依存関係を使用した際に発生した例外を受け取ることになります。
|
||||
|
||||
例えば、途中のどこかの時点で、別の依存関係や*path operation*の中で、データベーストランザクションを「ロールバック」したり、その他のエラーを作成したりするコードがあった場合、依存関係の中で例外を受け取ることになります。
|
||||
|
||||
そのため、依存関係の中にある特定の例外を`except SomeException`で探すことができます。
|
||||
|
||||
同様に、`finally`を用いて例外があったかどうかにかかわらず、終了ステップを確実に実行することができます。
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
|
||||
|
||||
## `yield`を持つサブ依存関係
|
||||
|
||||
任意の大きさや形のサブ依存関係やサブ依存関係の「ツリー」を持つことができ、その中で`yield`を使用することができます。
|
||||
|
||||
**FastAPI** は、`yield`を持つ各依存関係の「終了コード」が正しい順番で実行されていることを確認します。
|
||||
|
||||
例えば、`dependency_c`は`dependency_b`と`dependency_b`に依存する`dependency_a`に、依存することができます:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008.py hl[4,12,20] *}
|
||||
|
||||
そして、それらはすべて`yield`を使用することができます。
|
||||
|
||||
この場合、`dependency_c`は終了コードを実行するために、`dependency_b`(ここでは`dep_b`という名前)の値がまだ利用可能である必要があります。
|
||||
|
||||
そして、`dependency_b`は`dependency_a`(ここでは`dep_a`という名前)の値を終了コードで利用できるようにする必要があります。
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008.py hl[16,17,24,25] *}
|
||||
|
||||
同様に、`yield`と`return`が混在した依存関係を持つこともできます。
|
||||
|
||||
また、単一の依存関係を持っていて、`yield`などの他の依存関係をいくつか必要とすることもできます。
|
||||
|
||||
依存関係の組み合わせは自由です。
|
||||
|
||||
**FastAPI** は、全てが正しい順序で実行されていることを確認します。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
これはPythonの<a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">Context Managers</a>のおかげで動作します。
|
||||
|
||||
**FastAPI** はこれを実現するために内部的に使用しています。
|
||||
|
||||
///
|
||||
|
||||
## `yield`と`HTTPException`を持つ依存関係
|
||||
|
||||
`yield`と例外をキャッチする`try`ブロックを持つことができる依存関係を使用することができることがわかりました。
|
||||
|
||||
`yield`の後の終了コードで`HTTPException`などを発生させたくなるかもしれません。しかし**それはうまくいきません**
|
||||
|
||||
`yield`を持つ依存関係の終了コードは[例外ハンドラ](../handling-errors.md#_4){.internal-link target=_blank}の*後に*実行されます。依存関係によって投げられた例外を終了コード(`yield`の後)でキャッチするものはなにもありません。
|
||||
|
||||
つまり、`yield`の後に`HTTPException`を発生させた場合、`HTTTPException`をキャッチしてHTTP 400のレスポンスを返すデフォルトの(あるいは任意のカスタムの)例外ハンドラは、その例外をキャッチすることができなくなります。
|
||||
|
||||
これは、依存関係に設定されているもの(例えば、DBセッション)を、例えば、バックグラウンドタスクで使用できるようにするものです。
|
||||
|
||||
バックグラウンドタスクはレスポンスが送信された*後*に実行されます。そのため、*すでに送信されている*レスポンスを変更する方法すらないので、`HTTPException`を発生させる方法はありません。
|
||||
|
||||
しかし、バックグラウンドタスクがDBエラーを発生させた場合、少なくとも`yield`で依存関係のセッションをロールバックしたり、きれいに閉じたりすることができ、エラーをログに記録したり、リモートのトラッキングシステムに報告したりすることができます。
|
||||
|
||||
例外が発生する可能性があるコードがある場合は、最も普通の「Python流」なことをして、コードのその部分に`try`ブロックを追加してください。
|
||||
|
||||
レスポンスを返したり、レスポンスを変更したり、`HTTPException`を発生させたりする*前に*処理したいカスタム例外がある場合は、[カスタム例外ハンドラ](../handling-errors.md#_4){.internal-link target=_blank}を作成してください。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`HTTPException`を含む例外は、`yield`の*前*でも発生させることができます。ただし、後ではできません。
|
||||
|
||||
///
|
||||
|
||||
実行の順序は多かれ少なかれ以下の図のようになります。時間は上から下へと流れていきます。そして、各列はコードを相互作用させたり、実行したりしている部分の一つです。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
|
||||
participant client as Client
|
||||
participant handler as Exception handler
|
||||
participant dep as Dep with yield
|
||||
participant operation as Path Operation
|
||||
participant tasks as Background tasks
|
||||
|
||||
Note over client,tasks: Can raise exception for dependency, handled after response is sent
|
||||
Note over client,operation: Can raise HTTPException and can change the response
|
||||
client ->> dep: Start request
|
||||
Note over dep: Run code up to yield
|
||||
opt raise
|
||||
dep -->> handler: Raise HTTPException
|
||||
handler -->> client: HTTP error response
|
||||
dep -->> dep: Raise other exception
|
||||
end
|
||||
dep ->> operation: Run dependency, e.g. DB session
|
||||
opt raise
|
||||
operation -->> handler: Raise HTTPException
|
||||
handler -->> client: HTTP error response
|
||||
operation -->> dep: Raise other exception
|
||||
end
|
||||
operation ->> client: Return response to client
|
||||
Note over client,operation: Response is already sent, can't change it anymore
|
||||
opt Tasks
|
||||
operation -->> tasks: Send background tasks
|
||||
end
|
||||
opt Raise other exception
|
||||
tasks -->> dep: Raise other exception
|
||||
end
|
||||
Note over dep: After yield
|
||||
opt Handle other exception
|
||||
dep -->> dep: Handle exception, can't change response. E.g. close DB session.
|
||||
end
|
||||
```
|
||||
|
||||
/// info | 情報
|
||||
|
||||
**1つのレスポンス** だけがクライアントに送信されます。それはエラーレスポンスの一つかもしれませんし、*path operation*からのレスポンスかもしれません。
|
||||
|
||||
いずれかのレスポンスが送信された後、他のレスポンスを送信することはできません。
|
||||
|
||||
///
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
この図は`HTTPException`を示していますが、[カスタム例外ハンドラ](../handling-errors.md#_4){.internal-link target=_blank}を作成することで、他の例外を発生させることもできます。そして、その例外は依存関係の終了コードではなく、そのカスタム例外ハンドラによって処理されます。
|
||||
|
||||
しかし例外ハンドラで処理されない例外を発生させた場合は、依存関係の終了コードで処理されます。
|
||||
|
||||
///
|
||||
|
||||
## コンテキストマネージャ
|
||||
|
||||
### 「コンテキストマネージャ」とは
|
||||
|
||||
「コンテキストマネージャ」とは、`with`文の中で使用できるPythonオブジェクトのことです。
|
||||
|
||||
例えば、<a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank">ファイルを読み込むには`with`を使用することができます</a>:
|
||||
|
||||
```Python
|
||||
with open("./somefile.txt") as f:
|
||||
contents = f.read()
|
||||
print(contents)
|
||||
```
|
||||
|
||||
その後の`open("./somefile.txt")`は「コンテキストマネージャ」と呼ばれるオブジェクトを作成します。
|
||||
|
||||
`with`ブロックが終了すると、例外があったとしてもファイルを確かに閉じます。
|
||||
|
||||
`yield`を依存関係を作成すると、**FastAPI** は内部的にそれをコンテキストマネージャに変換し、他の関連ツールと組み合わせます。
|
||||
|
||||
### `yield`を持つ依存関係でのコンテキストマネージャの使用
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
これは多かれ少なかれ、「高度な」発想です。
|
||||
|
||||
**FastAPI** を使い始めたばかりの方は、とりあえずスキップした方がよいかもしれません。
|
||||
|
||||
///
|
||||
|
||||
Pythonでは、<a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">以下の2つのメソッドを持つクラスを作成する: `__enter__()`と`__exit__()`</a>ことでコンテキストマネージャを作成することができます。
|
||||
|
||||
また、依存関数の中で`with`や`async with`文を使用することによって`yield`を持つ **FastAPI** の依存関係の中でそれらを使用することができます:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial010.py hl[1,2,3,4,5,6,7,8,9,13] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
コンテキストマネージャを作成するもう一つの方法はwithです:
|
||||
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> または
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
|
||||
|
||||
これらを使って、関数を単一の`yield`でデコレートすることができます。
|
||||
|
||||
これは **FastAPI** が内部的に`yield`を持つ依存関係のために使用しているものです。
|
||||
|
||||
しかし、FastAPIの依存関係にデコレータを使う必要はありません(そして使うべきではありません)。
|
||||
|
||||
FastAPIが内部的にやってくれます。
|
||||
|
||||
///
|
||||
@@ -0,0 +1,212 @@
|
||||
# 依存関係 - 最初のステップ
|
||||
|
||||
** FastAPI** は非常に強力でありながら直感的な **<abbr title="コンポーネント、リソース、プロバイダ、サービス、インジェクタブルとしても知られている">依存性注入</abbr>** システムを持っています。
|
||||
|
||||
それは非常にシンプルに使用できるように設計されており、開発者が他のコンポーネント **FastAPI** と統合するのが非常に簡単になるように設計されています。
|
||||
|
||||
## 「依存性注入」とは
|
||||
|
||||
**「依存性注入」** とは、プログラミングにおいて、コード(この場合は、*path operation関数*)が動作したり使用したりするために必要なもの(「依存関係」)を宣言する方法があることを意味します:
|
||||
|
||||
そして、そのシステム(この場合は、**FastAPI**)は、必要な依存関係をコードに提供するために必要なことは何でも行います(依存関係を「注入」します)。
|
||||
|
||||
これは以下のようなことが必要な時にとても便利です:
|
||||
|
||||
* ロジックを共有している。(同じコードロジックを何度も繰り返している)。
|
||||
* データベース接続を共有する。
|
||||
* セキュリティ、認証、ロール要件などを強制する。
|
||||
* そのほかにも多くのこと...
|
||||
|
||||
これらすべてを、コードの繰り返しを最小限に抑えながら行います。
|
||||
|
||||
## 最初のステップ
|
||||
|
||||
非常にシンプルな例を見てみましょう。あまりにもシンプルなので、今のところはあまり参考にならないでしょう。
|
||||
|
||||
しかし、この方法では **依存性注入** システムがどのように機能するかに焦点を当てることができます。
|
||||
|
||||
### 依存関係の作成
|
||||
|
||||
まずは依存関係に注目してみましょう。
|
||||
|
||||
以下のように、*path operation関数*と同じパラメータを全て取ることができる関数にすぎません:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial001.py hl[8,9] *}
|
||||
|
||||
これだけです。
|
||||
|
||||
**2行**。
|
||||
|
||||
そして、それはすべての*path operation関数*が持っているのと同じ形と構造を持っています。
|
||||
|
||||
「デコレータ」を含まない(`@app.get("/some-path")`を含まない)*path operation関数*と考えることもできます。
|
||||
|
||||
そして何でも返すことができます。
|
||||
|
||||
この場合、この依存関係は以下を期待しています:
|
||||
|
||||
* オプショナルのクエリパラメータ`q`は`str`です。
|
||||
* オプショナルのクエリパラメータ`skip`は`int`で、デフォルトは`0`です。
|
||||
* オプショナルのクエリパラメータ`limit`は`int`で、デフォルトは`100`です。
|
||||
|
||||
そして、これらの値を含む`dict`を返します。
|
||||
|
||||
### `Depends`のインポート
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial001.py hl[3] *}
|
||||
|
||||
### "dependant"での依存関係の宣言
|
||||
|
||||
*path operation関数*のパラメータに`Body`や`Query`などを使用するのと同じように、新しいパラメータに`Depends`を使用することができます:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial001.py hl[13,18] *}
|
||||
|
||||
関数のパラメータに`Depends`を使用するのは`Body`や`Query`などと同じですが、`Depends`の動作は少し異なります。
|
||||
|
||||
`Depends`は1つのパラメータしか与えられません。
|
||||
|
||||
このパラメータは関数のようなものである必要があります。
|
||||
|
||||
そして、その関数は、*path operation関数*が行うのと同じ方法でパラメータを取ります。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
次の章では、関数以外の「もの」が依存関係として使用できるものを見ていきます。
|
||||
|
||||
///
|
||||
|
||||
新しいリクエストが到着するたびに、**FastAPI** が以下のような処理を行います:
|
||||
|
||||
* 依存関係("dependable")関数を正しいパラメータで呼び出します。
|
||||
* 関数の結果を取得します。
|
||||
* *path operation関数*のパラメータにその結果を代入してください。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
|
||||
common_parameters(["common_parameters"])
|
||||
read_items["/items/"]
|
||||
read_users["/users/"]
|
||||
|
||||
common_parameters --> read_items
|
||||
common_parameters --> read_users
|
||||
```
|
||||
|
||||
この方法では、共有されるコードを一度書き、**FastAPI** が*path operations*のための呼び出しを行います。
|
||||
|
||||
/// check | 確認
|
||||
|
||||
特別なクラスを作成してどこかで **FastAPI** に渡して「登録」する必要はないことに注意してください。
|
||||
|
||||
`Depends`を渡すだけで、**FastAPI** が残りの処理をしてくれます。
|
||||
|
||||
///
|
||||
|
||||
## `async`にするかどうか
|
||||
|
||||
依存関係は **FastAPI**(*path operation関数*と同じ)からも呼び出されるため、関数を定義する際にも同じルールが適用されます。
|
||||
|
||||
`async def`や通常の`def`を使用することができます。
|
||||
|
||||
また、通常の`def`*path operation関数*の中に`async def`を入れて依存関係を宣言したり、`async def`*path operation関数*の中に`def`を入れて依存関係を宣言したりすることなどができます。
|
||||
|
||||
それは重要ではありません。**FastAPI** は何をすべきかを知っています。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
わからない場合は、ドキュメントの[Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank}の中の`async`と`await`についてのセクションを確認してください。
|
||||
|
||||
///
|
||||
|
||||
## OpenAPIとの統合
|
||||
|
||||
依存関係(およびサブ依存関係)のすべてのリクエスト宣言、検証、および要件は、同じOpenAPIスキーマに統合されます。
|
||||
|
||||
つまり、対話型ドキュメントにはこれらの依存関係から得られる全ての情報も含まれているということです:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/dependencies/image01.png">
|
||||
|
||||
## 簡単な使い方
|
||||
|
||||
見てみると、*path*と*operation*が一致した時に*path operation関数*が宣言されていて、**FastAPI** が正しいパラメータで関数を呼び出してリクエストからデータを抽出する処理をしています。
|
||||
|
||||
実は、すべての(あるいはほとんどの)Webフレームワークは、このように動作します。
|
||||
|
||||
これらの関数を直接呼び出すことはありません。これらの関数はフレームワーク(この場合は、**FastAPI**)によって呼び出されます。
|
||||
|
||||
依存性注入システムでは、**FastAPI** に*path operation*もまた、*path operation関数*の前に実行されるべき他の何かに「依存」していることを伝えることができ、**FastAPI** がそれを実行し、結果を「注入」することを引き受けます。
|
||||
|
||||
他にも、「依存性注入」と同じような考えの一般的な用語があります:
|
||||
|
||||
* リソース
|
||||
* プロバイダ
|
||||
* サービス
|
||||
* インジェクタブル
|
||||
* コンポーネント
|
||||
|
||||
## **FastAPI** プラグイン
|
||||
|
||||
統合や「プラグイン」は **依存性注入** システムを使って構築することができます。しかし、実際には、**「プラグイン」を作成する必要はありません**。依存関係を使用することで、無限の数の統合やインタラクションを宣言することができ、それが**path operation関数*で利用可能になるからです。
|
||||
|
||||
依存関係は非常にシンプルで直感的な方法で作成することができ、必要なPythonパッケージをインポートするだけで、*文字通り*数行のコードでAPI関数と統合することができます。
|
||||
|
||||
次の章では、リレーショナルデータベースやNoSQLデータベース、セキュリティなどについて、その例を見ていきます。
|
||||
|
||||
## **FastAPI** 互換性
|
||||
|
||||
依存性注入システムがシンプルなので、**FastAPI** は以下のようなものと互換性があります:
|
||||
|
||||
* すべてのリレーショナルデータベース
|
||||
* NoSQLデータベース
|
||||
* 外部パッケージ
|
||||
* 外部API
|
||||
* 認証・認可システム
|
||||
* API利用状況監視システム
|
||||
* レスポンスデータ注入システム
|
||||
* など。
|
||||
|
||||
## シンプルでパワフル
|
||||
|
||||
階層依存性注入システムは、定義や使用方法が非常にシンプルであるにもかかわらず、非常に強力なものとなっています。
|
||||
|
||||
依存関係事態を定義する依存関係を定義することができます。
|
||||
|
||||
最終的には、依存関係の階層ツリーが構築され、**依存性注入**システムが、これらの依存関係(およびそのサブ依存関係)をすべて解決し、各ステップで結果を提供(注入)します。
|
||||
|
||||
例えば、4つのAPIエンドポイント(*path operations*)があるとします:
|
||||
|
||||
* `/items/public/`
|
||||
* `/items/private/`
|
||||
* `/users/{user_id}/activate`
|
||||
* `/items/pro/`
|
||||
|
||||
そして、依存関係とサブ依存関係だけで、それぞれに異なるパーミッション要件を追加することができます:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
|
||||
current_user(["current_user"])
|
||||
active_user(["active_user"])
|
||||
admin_user(["admin_user"])
|
||||
paying_user(["paying_user"])
|
||||
|
||||
public["/items/public/"]
|
||||
private["/items/private/"]
|
||||
activate_user["/users/{user_id}/activate"]
|
||||
pro_items["/items/pro/"]
|
||||
|
||||
current_user --> active_user
|
||||
active_user --> admin_user
|
||||
active_user --> paying_user
|
||||
|
||||
current_user --> public
|
||||
active_user --> private
|
||||
admin_user --> activate_user
|
||||
paying_user --> pro_items
|
||||
```
|
||||
|
||||
## **OpenAPI** との統合
|
||||
|
||||
これら全ての依存関係は、要件を宣言すると同時に、*path operations*にパラメータやバリデーションを追加します。
|
||||
|
||||
**FastAPI** はそれをすべてOpenAPIスキーマに追加して、対話型のドキュメントシステムに表示されるようにします。
|
||||
@@ -0,0 +1,86 @@
|
||||
# サブ依存関係
|
||||
|
||||
**サブ依存関係** を持つ依存関係を作成することができます。
|
||||
|
||||
それらは必要なだけ **深く** することができます。
|
||||
|
||||
**FastAPI** はそれらを解決してくれます。
|
||||
|
||||
### 最初の依存関係「依存可能なもの」
|
||||
|
||||
以下のような最初の依存関係(「依存可能なもの」)を作成することができます:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial005.py hl[8,9] *}
|
||||
|
||||
これはオプショナルのクエリパラメータ`q`を`str`として宣言し、それを返すだけです。
|
||||
|
||||
これは非常にシンプルです(あまり便利ではありません)が、サブ依存関係がどのように機能するかに焦点を当てるのに役立ちます。
|
||||
|
||||
### 第二の依存関係 「依存可能なもの」と「依存」
|
||||
|
||||
そして、別の依存関数(「依存可能なもの」)を作成して、同時にそれ自身の依存関係を宣言することができます(つまりそれ自身も「依存」です):
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial005.py hl[13] *}
|
||||
|
||||
宣言されたパラメータに注目してみましょう:
|
||||
|
||||
* この関数は依存関係(「依存可能なもの」)そのものであるにもかかわらず、別の依存関係を宣言しています(何か他のものに「依存」しています)。
|
||||
* これは`query_extractor`に依存しており、それが返す値をパラメータ`q`に代入します。
|
||||
* また、オプショナルの`last_query`クッキーを`str`として宣言します。
|
||||
* ユーザーがクエリ`q`を提供しなかった場合、クッキーに保存していた最後に使用したクエリを使用します。
|
||||
|
||||
### 依存関係の使用
|
||||
|
||||
以下のように依存関係を使用することができます:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial005.py hl[21] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
*path operation関数*の中で宣言している依存関係は`query_or_cookie_extractor`の1つだけであることに注意してください。
|
||||
|
||||
しかし、**FastAPI** は`query_extractor`を最初に解決し、その結果を`query_or_cookie_extractor`を呼び出す時に渡す必要があることを知っています。
|
||||
|
||||
///
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
|
||||
query_extractor(["query_extractor"])
|
||||
query_or_cookie_extractor(["query_or_cookie_extractor"])
|
||||
|
||||
read_query["/items/"]
|
||||
|
||||
query_extractor --> query_or_cookie_extractor --> read_query
|
||||
```
|
||||
|
||||
## 同じ依存関係の複数回の使用
|
||||
|
||||
依存関係の1つが同じ*path operation*に対して複数回宣言されている場合、例えば、複数の依存関係が共通のサブ依存関係を持っている場合、**FastAPI** はリクエストごとに1回だけそのサブ依存関係を呼び出します。
|
||||
|
||||
そして、返された値を<abbr title="計算された値・生成された値を保存するユーティリティまたはシステム、再計算する代わりに再利用するためのもの">「キャッシュ」</abbr>に保存し、同じリクエストに対して依存関係を何度も呼び出す代わりに、特定のリクエストでそれを必要とする全ての「依存関係」に渡すことになります。
|
||||
|
||||
高度なシナリオでは、「キャッシュされた」値を使うのではなく、同じリクエストの各ステップ(おそらく複数回)で依存関係を呼び出す必要があることがわかっている場合、`Depens`を使用する際に、`use_cache=False`というパラメータを設定することができます。
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
|
||||
return {"fresh_value": fresh_value}
|
||||
```
|
||||
|
||||
## まとめ
|
||||
|
||||
ここで使われている派手な言葉は別にして、**依存性注入** システムは非常にシンプルです。
|
||||
|
||||
*path operation関数*と同じように見えるただの関数です。
|
||||
|
||||
しかし、それでも非常に強力で、任意の深くネストされた依存関係「グラフ」(ツリー)を宣言することができます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
これらの単純な例では、全てが役に立つとは言えないかもしれません。
|
||||
|
||||
しかし、**security** についての章で、それがどれほど有用であるかがわかるでしょう。
|
||||
|
||||
そして、あなたを救うコードの量もみることになるでしょう。
|
||||
|
||||
///
|
||||
@@ -0,0 +1,35 @@
|
||||
# JSON互換エンコーダ
|
||||
|
||||
データ型(Pydanticモデルのような)をJSONと互換性のあるもの(`dict`や`list`など)に変更する必要がある場合があります。
|
||||
|
||||
例えば、データベースに保存する必要がある場合です。
|
||||
|
||||
そのために、**FastAPI** は`jsonable_encoder()`関数を提供しています。
|
||||
|
||||
## `jsonable_encoder`の使用
|
||||
|
||||
JSON互換のデータのみを受信するデータベース`fake_db`があるとしましょう。
|
||||
|
||||
例えば、`datetime`オブジェクトはJSONと互換性がないので、このデーターベースには受け取られません。
|
||||
|
||||
そのため、`datetime`オブジェクトは<a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">ISO形式</a>のデータを含む`str`に変換されなければなりません。
|
||||
|
||||
同様に、このデータベースはPydanticモデル(属性を持つオブジェクト)を受け取らず、`dict`だけを受け取ります。
|
||||
|
||||
そのために`jsonable_encoder`を使用することができます。
|
||||
|
||||
Pydanticモデルのようなオブジェクトを受け取り、JSON互換版を返します:
|
||||
|
||||
{* ../../docs_src/encoder/tutorial001.py hl[5,22] *}
|
||||
|
||||
この例では、Pydanticモデルを`dict`に、`datetime`を`str`に変換します。
|
||||
|
||||
呼び出した結果は、Pythonの標準の<a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>でエンコードできるものです。
|
||||
|
||||
これはJSON形式のデータを含む大きな`str`を(文字列として)返しません。JSONと互換性のある値とサブの値を持つPython標準のデータ構造(例:`dict`)を返します。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
`jsonable_encoder`は実際には **FastAPI** が内部的にデータを変換するために使用します。しかしこれは他の多くのシナリオで有用です。
|
||||
|
||||
///
|
||||
@@ -0,0 +1,62 @@
|
||||
# 追加データ型
|
||||
|
||||
今までは、以下のような一般的なデータ型を使用してきました:
|
||||
|
||||
* `int`
|
||||
* `float`
|
||||
* `str`
|
||||
* `bool`
|
||||
|
||||
しかし、より複雑なデータ型を使用することもできます。
|
||||
|
||||
そして、今まで見てきたのと同じ機能を持つことになります:
|
||||
|
||||
* 素晴らしいエディタのサポート
|
||||
* 受信したリクエストからのデータ変換
|
||||
* レスポンスデータのデータ変換
|
||||
* データの検証
|
||||
* 自動注釈と文書化
|
||||
|
||||
## 他のデータ型
|
||||
|
||||
ここでは、使用できる追加のデータ型のいくつかを紹介します:
|
||||
|
||||
* `UUID`:
|
||||
* 多くのデータベースやシステムで共通のIDとして使用される、標準的な「ユニバーサルにユニークな識別子」です。
|
||||
* リクエストとレスポンスでは`str`として表現されます。
|
||||
* `datetime.datetime`:
|
||||
* Pythonの`datetime.datetime`です。
|
||||
* リクエストとレスポンスはISO 8601形式の`str`で表現されます: `2008-09-15T15:53:00+05:00`
|
||||
* `datetime.date`:
|
||||
* Pythonの`datetime.date`です。
|
||||
* リクエストとレスポンスはISO 8601形式の`str`で表現されます: `2008-09-15`
|
||||
* `datetime.time`:
|
||||
* Pythonの`datetime.time`.
|
||||
* リクエストとレスポンスはISO 8601形式の`str`で表現されます: `14:23:55.003`
|
||||
* `datetime.timedelta`:
|
||||
* Pythonの`datetime.timedelta`です。
|
||||
* リクエストとレスポンスでは合計秒数の`float`で表現されます。
|
||||
* Pydanticでは「ISO 8601 time diff encoding」として表現することも可能です。<a href="https://docs.pydantic.dev/latest/concepts/serialization/" class="external-link" target="_blank">詳細はドキュメントを参照してください</a>。
|
||||
* `frozenset`:
|
||||
* リクエストとレスポンスでは`set`と同じように扱われます:
|
||||
* リクエストでは、リストが読み込まれ、重複を排除して`set`に変換されます。
|
||||
* レスポンスでは`set`が`list`に変換されます。
|
||||
* 生成されたスキーマは`set`の値が一意であることを指定します(JSON Schemaの`uniqueItems`を使用します)。
|
||||
* `bytes`:
|
||||
* Pythonの標準的な`bytes`です。
|
||||
* リクエストとレスポンスでは`str`として扱われます。
|
||||
* 生成されたスキーマは`str`で`binary`の「フォーマット」持つことを指定します。
|
||||
* `Decimal`:
|
||||
* Pythonの標準的な`Decimal`です。
|
||||
* リクエストやレスポンスでは`float`と同じように扱います。
|
||||
|
||||
* Pydanticの全ての有効な型はこちらで確認できます: <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">Pydantic data types</a>。
|
||||
## 例
|
||||
|
||||
ここでは、上記の型のいくつかを使用したパラメータを持つ*path operation*の例を示します。
|
||||
|
||||
{* ../../docs_src/extra_data_types/tutorial001.py hl[1,2,12:16] *}
|
||||
|
||||
関数内のパラメータは自然なデータ型を持っていることに注意してください。そして、以下のように通常の日付操作を行うことができます:
|
||||
|
||||
{* ../../docs_src/extra_data_types/tutorial001.py hl[18,19] *}
|
||||
@@ -0,0 +1,191 @@
|
||||
# モデル - より詳しく
|
||||
|
||||
先ほどの例に続き、複数の関連モデルを持つことが一般的です。
|
||||
|
||||
これはユーザーモデルの場合は特にそうです。なぜなら:
|
||||
|
||||
* **入力モデル** にはパスワードが必要です。
|
||||
* **出力モデル**はパスワードをもつべきではありません。
|
||||
* **データベースモデル**はおそらくハッシュ化されたパスワードが必要になるでしょう。
|
||||
|
||||
/// danger | 危険
|
||||
|
||||
ユーザーの平文のパスワードは絶対に保存しないでください。常に認証に利用可能な「安全なハッシュ」を保存してください。
|
||||
|
||||
知らない方は、[セキュリティの章](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}で「パスワードハッシュ」とは何かを学ぶことができます。
|
||||
|
||||
///
|
||||
|
||||
## 複数のモデル
|
||||
|
||||
ここでは、パスワードフィールドをもつモデルがどのように見えるのか、また、どこで使われるのか、大まかなイメージを紹介します:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial001.py hl[9,11,16,22,24,29:30,33:35,40:41] *}
|
||||
|
||||
### `**user_in.dict()`について
|
||||
|
||||
#### Pydanticの`.dict()`
|
||||
|
||||
`user_in`は`UserIn`クラスのPydanticモデルです。
|
||||
|
||||
Pydanticモデルには、モデルのデータを含む`dict`を返す`.dict()`メソッドがあります。
|
||||
|
||||
そこで、以下のようなPydanticオブジェクト`user_in`を作成すると:
|
||||
|
||||
```Python
|
||||
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
|
||||
```
|
||||
|
||||
そして呼び出すと:
|
||||
|
||||
```Python
|
||||
user_dict = user_in.dict()
|
||||
```
|
||||
|
||||
これで変数`user_dict`のデータを持つ`dict`ができました。(これはPydanticモデルのオブジェクトの代わりに`dict`です)。
|
||||
|
||||
そして呼び出すと:
|
||||
|
||||
```Python
|
||||
print(user_dict)
|
||||
```
|
||||
|
||||
以下のようなPythonの`dict`を得ることができます:
|
||||
|
||||
```Python
|
||||
{
|
||||
'username': 'john',
|
||||
'password': 'secret',
|
||||
'email': 'john.doe@example.com',
|
||||
'full_name': None,
|
||||
}
|
||||
```
|
||||
|
||||
#### `dict`の展開
|
||||
|
||||
`user_dict`のような`dict`を受け取り、それを`**user_dict`を持つ関数(またはクラス)に渡すと、Pythonはそれを「展開」します。これは`user_dict`のキーと値を直接キー・バリューの引数として渡します。
|
||||
|
||||
そこで上述の`user_dict`の続きを以下のように書くと:
|
||||
|
||||
```Python
|
||||
UserInDB(**user_dict)
|
||||
```
|
||||
|
||||
以下と同等の結果になります:
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
username="john",
|
||||
password="secret",
|
||||
email="john.doe@example.com",
|
||||
full_name=None,
|
||||
)
|
||||
```
|
||||
|
||||
もっと正確に言えば、`user_dict`を将来的にどんな内容であっても直接使用することになります:
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
username = user_dict["username"],
|
||||
password = user_dict["password"],
|
||||
email = user_dict["email"],
|
||||
full_name = user_dict["full_name"],
|
||||
)
|
||||
```
|
||||
|
||||
#### 別のモデルからつくるPydanticモデル
|
||||
|
||||
上述の例では`user_in.dict()`から`user_dict`をこのコードのように取得していますが:
|
||||
|
||||
```Python
|
||||
user_dict = user_in.dict()
|
||||
UserInDB(**user_dict)
|
||||
```
|
||||
|
||||
これは以下と同等です:
|
||||
|
||||
```Python
|
||||
UserInDB(**user_in.dict())
|
||||
```
|
||||
|
||||
...なぜなら`user_in.dict()`は`dict`であり、`**`を付与して`UserInDB`を渡してPythonに「展開」させているからです。
|
||||
|
||||
そこで、別のPydanticモデルのデータからPydanticモデルを取得します。
|
||||
|
||||
#### `dict`の展開と追加引数
|
||||
|
||||
そして、追加のキーワード引数`hashed_password=hashed_password`を以下のように追加すると:
|
||||
|
||||
```Python
|
||||
UserInDB(**user_in.dict(), hashed_password=hashed_password)
|
||||
```
|
||||
|
||||
...以下のようになります:
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
username = user_dict["username"],
|
||||
password = user_dict["password"],
|
||||
email = user_dict["email"],
|
||||
full_name = user_dict["full_name"],
|
||||
hashed_password = hashed_password,
|
||||
)
|
||||
```
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
サポートしている追加機能は、データの可能な流れをデモするだけであり、もちろん本当のセキュリティを提供しているわけではありません。
|
||||
|
||||
///
|
||||
|
||||
## 重複の削減
|
||||
|
||||
コードの重複を減らすことは、**FastAPI**の中核的なアイデアの1つです。
|
||||
|
||||
コードの重複が増えると、バグやセキュリティの問題、コードの非同期化問題(ある場所では更新しても他の場所では更新されない場合)などが発生する可能性が高くなります。
|
||||
|
||||
そして、これらのモデルは全てのデータを共有し、属性名や型を重複させています。
|
||||
|
||||
もっと良い方法があります。
|
||||
|
||||
他のモデルのベースとなる`UserBase`モデルを宣言することができます。そして、そのモデルの属性(型宣言、検証など)を継承するサブクラスを作ることができます。
|
||||
|
||||
データの変換、検証、文書化などはすべて通常通りに動作します。
|
||||
|
||||
このようにして、モデル間の違いだけを宣言することができます:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial002.py hl[9,15,16,19,20,23,24] *}
|
||||
|
||||
## `Union`または`anyOf`
|
||||
|
||||
レスポンスを2つの型の`Union`として宣言することができます。
|
||||
|
||||
OpenAPIでは`anyOf`で定義されます。
|
||||
|
||||
そのためには、標準的なPythonの型ヒント<a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>を使用します:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial003.py hl[1,14,15,18,19,20,33] *}
|
||||
|
||||
## モデルのリスト
|
||||
|
||||
同じように、オブジェクトのリストのレスポンスを宣言することができます。
|
||||
|
||||
そのためには、標準のPythonの`typing.List`を使用する:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial004.py hl[1,20] *}
|
||||
|
||||
## 任意の`dict`を持つレスポンス
|
||||
|
||||
また、Pydanticモデルを使用せずに、キーと値の型だけを定義した任意の`dict`を使ってレスポンスを宣言することもできます。
|
||||
|
||||
これは、有効なフィールド・属性名(Pydanticモデルに必要なもの)を事前に知らない場合に便利です。
|
||||
|
||||
この場合、`typing.Dict`を使用することができます:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial005.py hl[1,8] *}
|
||||
|
||||
## まとめ
|
||||
|
||||
複数のPydanticモデルを使用し、ケースごとに自由に継承します。
|
||||
|
||||
エンティティが異なる「状態」を持たなければならない場合は、エンティティごとに単一のデータモデルを持つ必要はありません。`password` や `password_hash` やパスワードなしなどのいくつかの「状態」をもつユーザー「エンティティ」の場合の様にすれば良いです。
|
||||
@@ -0,0 +1,333 @@
|
||||
# 最初のステップ
|
||||
|
||||
最もシンプルなFastAPIファイルは以下のようになります:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py *}
|
||||
|
||||
これを`main.py`にコピーします。
|
||||
|
||||
ライブサーバーを実行します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
<span style="color: green;">INFO</span>: Started reloader process [28720]
|
||||
<span style="color: green;">INFO</span>: Started server process [28722]
|
||||
<span style="color: green;">INFO</span>: Waiting for application startup.
|
||||
<span style="color: green;">INFO</span>: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// note | 備考
|
||||
|
||||
`uvicorn main:app`は以下を示します:
|
||||
|
||||
* `main`: `main.py`ファイル (Python "module")。
|
||||
* `app`: `main.py`内部で作られるobject(`app = FastAPI()`のように記述される)。
|
||||
* `--reload`: コードの変更時にサーバーを再起動させる。開発用。
|
||||
|
||||
///
|
||||
|
||||
出力には次のような行があります:
|
||||
|
||||
```hl_lines="4"
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
この行はローカルマシンでアプリが提供されているURLを示しています。
|
||||
|
||||
### チェック
|
||||
|
||||
ブラウザで<a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>を開きます。
|
||||
|
||||
次のようなJSONレスポンスが表示されます:
|
||||
|
||||
```JSON
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
### 対話的APIドキュメント
|
||||
|
||||
次に、<a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>にアクセスします。
|
||||
|
||||
自動生成された対話的APIドキュメントが表示されます (<a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>で提供):
|
||||
|
||||

|
||||
|
||||
### 他のAPIドキュメント
|
||||
|
||||
次に、<a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>にアクセスします。
|
||||
|
||||
先ほどとは異なる、自動生成された対話的APIドキュメントが表示されます (<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>によって提供):
|
||||
|
||||

|
||||
|
||||
### OpenAPI
|
||||
|
||||
**FastAPI**は、APIを定義するための**OpenAPI**標準規格を使用して、すべてのAPIの「スキーマ」を生成します。
|
||||
|
||||
#### 「スキーマ」
|
||||
|
||||
「スキーマ」は定義または説明です。実装コードではなく、単なる抽象的な説明です。
|
||||
|
||||
#### API「スキーマ」
|
||||
|
||||
ここでは、<a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a>はAPIのスキーマ定義の方法を規定する仕様です。
|
||||
|
||||
このスキーマ定義はAPIパス、受け取り可能なパラメータなどが含まれます。
|
||||
|
||||
#### データ「スキーマ」
|
||||
|
||||
「スキーマ」という用語は、JSONコンテンツなどの一部のデータの形状を指す場合もあります。
|
||||
|
||||
そのような場合、スキーマはJSON属性とそれらが持つデータ型などを意味します。
|
||||
|
||||
#### OpenAPIおよびJSONスキーマ
|
||||
|
||||
OpenAPIはAPIのためのAPIスキーマを定義します。そして、そのスキーマは**JSONデータスキーマ**の標準規格に準拠したJSONスキーマを利用するAPIによって送受されるデータの定義(または「スキーマ」)を含んでいます。
|
||||
|
||||
#### `openapi.json`を確認
|
||||
|
||||
素のOpenAPIスキーマがどのようなものか興味がある場合、FastAPIはすべてのAPIの説明を含むJSON(スキーマ)を自動的に生成します。
|
||||
|
||||
次の場所で直接確認できます: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
|
||||
|
||||
次のようなJSONが表示されます。
|
||||
|
||||
```JSON
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
"info": {
|
||||
"title": "FastAPI",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
|
||||
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### OpenAPIの目的
|
||||
|
||||
OpenAPIスキーマは、FastAPIに含まれている2つのインタラクティブなドキュメントシステムの動力源です。
|
||||
|
||||
そして、OpenAPIに基づいた代替案が数十通りあります。 **FastAPI**で構築されたアプリケーションに、これらの選択肢を簡単に追加できます。
|
||||
|
||||
また、APIと通信するクライアント用のコードを自動的に生成するために使用することもできます。たとえば、フロントエンド、モバイル、またはIoTアプリケーションです。
|
||||
|
||||
## ステップ毎の要約
|
||||
|
||||
### Step 1: `FastAPI`をインポート
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
|
||||
|
||||
`FastAPI`は、APIのすべての機能を提供するPythonクラスです。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`FastAPI`は`Starlette`を直接継承するクラスです。
|
||||
|
||||
`FastAPI`でも<a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a>のすべての機能を利用可能です。
|
||||
|
||||
///
|
||||
|
||||
### Step 2: `FastAPI`の「インスタンス」を生成
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
|
||||
ここで、`app`変数が`FastAPI`クラスの「インスタンス」になります。
|
||||
|
||||
これが、すべてのAPIを作成するための主要なポイントになります。
|
||||
|
||||
この`app`はコマンドで`uvicorn`が参照するものと同じです:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
以下のようなアプリを作成したとき:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial002.py hl[3] *}
|
||||
|
||||
そして、それを`main.py`ファイルに置き、次のように`uvicorn`を呼び出します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:my_awesome_api --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Step 3: *path operation*を作成
|
||||
|
||||
#### パス
|
||||
|
||||
ここでの「パス」とは、最初の`/`から始まるURLの最後の部分を指します。
|
||||
|
||||
したがって、次のようなURLでは:
|
||||
|
||||
```
|
||||
https://example.com/items/foo
|
||||
```
|
||||
|
||||
...パスは次のようになります:
|
||||
|
||||
```
|
||||
/items/foo
|
||||
```
|
||||
|
||||
/// info | 情報
|
||||
|
||||
「パス」は一般に「エンドポイント」または「ルート」とも呼ばれます。
|
||||
|
||||
///
|
||||
|
||||
APIを構築する際、「パス」は「関心事」と「リソース」を分離するための主要な方法です。
|
||||
|
||||
#### Operation
|
||||
|
||||
ここでの「オペレーション」とは、HTTPの「メソッド」の1つを指します。
|
||||
|
||||
以下のようなものの1つ:
|
||||
|
||||
* `POST`
|
||||
* `GET`
|
||||
* `PUT`
|
||||
* `DELETE`
|
||||
|
||||
...さらによりエキゾチックなもの:
|
||||
|
||||
* `OPTIONS`
|
||||
* `HEAD`
|
||||
* `PATCH`
|
||||
* `TRACE`
|
||||
|
||||
HTTPプロトコルでは、これらの「メソッド」の1つ(または複数)を使用して各パスにアクセスできます。
|
||||
|
||||
---
|
||||
|
||||
APIを構築するときは、通常、これらの特定のHTTPメソッドを使用して特定のアクションを実行します。
|
||||
|
||||
通常は次を使用します:
|
||||
|
||||
* `POST`: データの作成
|
||||
* `GET`: データの読み取り
|
||||
* `PUT`: データの更新
|
||||
* `DELETE`: データの削除
|
||||
|
||||
したがって、OpenAPIでは、各HTTPメソッドは「オペレーション」と呼ばれます。
|
||||
|
||||
「**オペレーションズ**」とも呼ぶことにします。
|
||||
|
||||
#### *パスオペレーションデコレータ*を定義
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
|
||||
`@app.get("/")`は直下の関数が下記のリクエストの処理を担当することを**FastAPI**に伝えます:
|
||||
|
||||
* パス `/`
|
||||
* <abbr title="an HTTP GET method"><code>get</code> オペレーション</abbr>
|
||||
|
||||
/// info | `@decorator` について
|
||||
|
||||
Pythonにおける`@something`シンタックスはデコレータと呼ばれます。
|
||||
|
||||
「デコレータ」は関数の上に置きます。かわいらしい装飾的な帽子のようです(この用語の由来はそこにあると思います)。
|
||||
|
||||
「デコレータ」は直下の関数を受け取り、それを使って何かを行います。
|
||||
|
||||
私たちの場合、このデコレーターは直下の関数が**オペレーション** `get`を使用した**パス**` / `に対応することを**FastAPI** に通知します。
|
||||
|
||||
これが「*パスオペレーションデコレータ*」です。
|
||||
|
||||
///
|
||||
|
||||
他のオペレーションも使用できます:
|
||||
|
||||
* `@app.post()`
|
||||
* `@app.put()`
|
||||
* `@app.delete()`
|
||||
|
||||
また、よりエキゾチックなものも使用できます:
|
||||
|
||||
* `@app.options()`
|
||||
* `@app.head()`
|
||||
* `@app.patch()`
|
||||
* `@app.trace()`
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
各オペレーション (HTTPメソッド)は自由に使用できます。
|
||||
|
||||
**FastAPI**は特定の意味づけを強制しません。
|
||||
|
||||
ここでの情報は、要件ではなくガイドラインとして提示されます。
|
||||
|
||||
例えば、GraphQLを使用する場合、通常は`POST`オペレーションのみを使用してすべてのアクションを実行します。
|
||||
|
||||
///
|
||||
|
||||
### Step 4: **パスオペレーション**を定義
|
||||
|
||||
以下は「**パスオペレーション関数**」です:
|
||||
|
||||
* **パス**: は`/`です。
|
||||
* **オペレーション**: は`get`です。
|
||||
* **関数**: 「デコレータ」の直下にある関数 (`@app.get("/")`の直下) です。
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
|
||||
|
||||
これは、Pythonの関数です。
|
||||
|
||||
この関数は、`GET`オペレーションを使ったURL「`/`」へのリクエストを受け取るたびに**FastAPI**によって呼び出されます。
|
||||
|
||||
この場合、この関数は`async`関数です。
|
||||
|
||||
---
|
||||
|
||||
`async def`の代わりに通常の関数として定義することもできます:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
|
||||
|
||||
/// note | 備考
|
||||
|
||||
違いが分からない場合は、[Async: *"急いでいますか?"*](../async.md#_1){.internal-link target=_blank}を確認してください。
|
||||
|
||||
///
|
||||
|
||||
### Step 5: コンテンツの返信
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
|
||||
|
||||
`dict`、`list`、`str`、`int`などを返すことができます。
|
||||
|
||||
Pydanticモデルを返すこともできます(後で詳しく説明します)。
|
||||
|
||||
JSONに自動的に変換されるオブジェクトやモデルは他にもたくさんあります(ORMなど)。 お気に入りのものを使ってみてください。すでにサポートされている可能性が高いです。
|
||||
|
||||
## まとめ
|
||||
|
||||
* `FastAPI`をインポート
|
||||
* `app`インスタンスを生成
|
||||
* **パスオペレーションデコレータ**を記述 (`@app.get("/")`)
|
||||
* **パスオペレーション関数**を定義 (上記の`def root(): ...`のように)
|
||||
* 開発サーバーを起動 (`uvicorn main:app --reload`)
|
||||
@@ -0,0 +1,261 @@
|
||||
# エラーハンドリング
|
||||
|
||||
APIを使用しているクライアントにエラーを通知する必要がある状況はたくさんあります。
|
||||
|
||||
このクライアントは、フロントエンドを持つブラウザ、誰かのコード、IoTデバイスなどが考えられます。
|
||||
|
||||
クライアントに以下のようなことを伝える必要があるかもしれません:
|
||||
|
||||
* クライアントにはその操作のための十分な権限がありません。
|
||||
* クライアントはそのリソースにアクセスできません。
|
||||
* クライアントがアクセスしようとしていた項目が存在しません。
|
||||
* など
|
||||
|
||||
これらの場合、通常は **400**(400から499)の範囲内の **HTTPステータスコード** を返すことになります。
|
||||
|
||||
これは200のHTTPステータスコード(200から299)に似ています。これらの「200」ステータスコードは、何らかの形でリクエスト「成功」であったことを意味します。
|
||||
|
||||
400の範囲にあるステータスコードは、クライアントからのエラーがあったことを意味します。
|
||||
|
||||
**"404 Not Found"** のエラー(およびジョーク)を覚えていますか?
|
||||
|
||||
## `HTTPException`の使用
|
||||
|
||||
HTTPレスポンスをエラーでクライアントに返すには、`HTTPException`を使用します。
|
||||
|
||||
### `HTTPException`のインポート
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
|
||||
|
||||
### コード内での`HTTPException`の発生
|
||||
|
||||
`HTTPException`は通常のPythonの例外であり、APIに関連するデータを追加したものです。
|
||||
|
||||
Pythonの例外なので、`return`ではなく、`raise`です。
|
||||
|
||||
これはまた、*path operation関数*の内部で呼び出しているユーティリティ関数の内部から`HTTPException`を発生させた場合、*path operation関数*の残りのコードは実行されず、そのリクエストを直ちに終了させ、`HTTPException`からのHTTPエラーをクライアントに送信することを意味します。
|
||||
|
||||
値を返す`return`よりも例外を発生させることの利点は、「依存関係とセキュリティ」のセクションでより明確になります。
|
||||
|
||||
この例では、クライアントが存在しないIDでアイテムを要求した場合、`404`のステータスコードを持つ例外を発生させます:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
|
||||
|
||||
### レスポンス結果
|
||||
|
||||
クライアントが`http://example.com/items/foo`(`item_id` `"foo"`)をリクエストすると、HTTPステータスコードが200で、以下のJSONレスポンスが返されます:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item": "The Foo Wrestlers"
|
||||
}
|
||||
```
|
||||
|
||||
しかし、クライアントが`http://example.com/items/bar`(存在しない`item_id` `"bar"`)をリクエストした場合、HTTPステータスコード404("not found"エラー)と以下のJSONレスポンスが返されます:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Item not found"
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`HTTPException`を発生させる際には、`str`だけでなく、JSONに変換できる任意の値を`detail`パラメータとして渡すことができます。
|
||||
|
||||
`dict`や`list`などを渡すことができます。
|
||||
|
||||
これらは **FastAPI** によって自動的に処理され、JSONに変換されます。
|
||||
|
||||
///
|
||||
|
||||
## カスタムヘッダーの追加
|
||||
|
||||
例えば、いくつかのタイプのセキュリティのために、HTTPエラーにカスタムヘッダを追加できると便利な状況がいくつかあります。
|
||||
|
||||
おそらくコードの中で直接使用する必要はないでしょう。
|
||||
|
||||
しかし、高度なシナリオのために必要な場合には、カスタムヘッダーを追加することができます:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
|
||||
|
||||
## カスタム例外ハンドラのインストール
|
||||
|
||||
カスタム例外ハンドラは<a href="https://www.starlette.dev/exceptions/" class="external-link" target="_blank">Starletteと同じ例外ユーティリティ</a>を使用して追加することができます。
|
||||
|
||||
あなた(または使用しているライブラリ)が`raise`するかもしれないカスタム例外`UnicornException`があるとしましょう。
|
||||
|
||||
そして、この例外をFastAPIでグローバルに処理したいと思います。
|
||||
|
||||
カスタム例外ハンドラを`@app.exception_handler()`で追加することができます:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial003.py hl[5,6,7,13,14,15,16,17,18,24] *}
|
||||
|
||||
ここで、`/unicorns/yolo`をリクエストすると、*path operation*は`UnicornException`を`raise`します。
|
||||
|
||||
しかし、これは`unicorn_exception_handler`で処理されます。
|
||||
|
||||
そのため、HTTPステータスコードが`418`で、JSONの内容が以下のような明確なエラーを受け取ることになります:
|
||||
|
||||
```JSON
|
||||
{"message": "Oops! yolo did something. There goes a rainbow..."}
|
||||
```
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
また、`from starlette.requests import Request`と`from starlette.responses import JSONResponse`を使用することもできます。
|
||||
|
||||
**FastAPI** は開発者の利便性を考慮して、`fastapi.responses`と同じ`starlette.responses`を提供しています。しかし、利用可能なレスポンスのほとんどはStarletteから直接提供されます。これは`Request`と同じです。
|
||||
|
||||
///
|
||||
|
||||
## デフォルトの例外ハンドラのオーバーライド
|
||||
|
||||
**FastAPI** にはいくつかのデフォルトの例外ハンドラがあります。
|
||||
|
||||
これらのハンドラは、`HTTPException`を`raise`させた場合や、リクエストに無効なデータが含まれている場合にデフォルトのJSONレスポンスを返す役割を担っています。
|
||||
|
||||
これらの例外ハンドラを独自のものでオーバーライドすることができます。
|
||||
|
||||
### リクエスト検証の例外のオーバーライド
|
||||
|
||||
リクエストに無効なデータが含まれている場合、**FastAPI** は内部的に`RequestValidationError`を発生させます。
|
||||
|
||||
また、そのためのデフォルトの例外ハンドラも含まれています。
|
||||
|
||||
これをオーバーライドするには`RequestValidationError`をインポートして`@app.exception_handler(RequestValidationError)`と一緒に使用して例外ハンドラをデコレートします。
|
||||
|
||||
この例外ハンドラは`Requset`と例外を受け取ります。
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004.py hl[2,14,15,16] *}
|
||||
|
||||
これで、`/items/foo`にアクセスすると、デフォルトのJSONエラーの代わりに以下が返されます:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"loc": [
|
||||
"path",
|
||||
"item_id"
|
||||
],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
以下のようなテキスト版を取得します:
|
||||
|
||||
```
|
||||
1 validation error
|
||||
path -> item_id
|
||||
value is not a valid integer (type=type_error.integer)
|
||||
```
|
||||
|
||||
#### `RequestValidationError`と`ValidationError`
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
これらは今のあなたにとって重要でない場合は省略しても良い技術的な詳細です。
|
||||
|
||||
///
|
||||
|
||||
`RequestValidationError`はPydanticの<a href="https://docs.pydantic.dev/latest/concepts/models/#error-handling" class="external-link" target="_blank">`ValidationError`</a>のサブクラスです。
|
||||
|
||||
**FastAPI** は`response_model`でPydanticモデルを使用していて、データにエラーがあった場合、ログにエラーが表示されるようにこれを使用しています。
|
||||
|
||||
しかし、クライアントやユーザーはそれを見ることはありません。その代わりに、クライアントはHTTPステータスコード`500`の「Internal Server Error」を受け取ります。
|
||||
|
||||
*レスポンス*やコードのどこか(クライアントの*リクエスト*ではなく)にPydanticの`ValidationError`がある場合、それは実際にはコードのバグなのでこのようにすべきです。
|
||||
|
||||
また、あなたがそれを修正している間は、セキュリティの脆弱性が露呈する場合があるため、クライアントやユーザーがエラーに関する内部情報にアクセスできないようにしてください。
|
||||
|
||||
### エラーハンドラ`HTTPException`のオーバーライド
|
||||
|
||||
同様に、`HTTPException`ハンドラをオーバーライドすることもできます。
|
||||
|
||||
例えば、これらのエラーに対しては、JSONではなくプレーンテキストを返すようにすることができます:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004.py hl[3,4,9,10,11,22] *}
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
また、`from starlette.responses import PlainTextResponse`を使用することもできます。
|
||||
|
||||
**FastAPI** は開発者の利便性を考慮して、`fastapi.responses`と同じ`starlette.responses`を提供しています。しかし、利用可能なレスポンスのほとんどはStarletteから直接提供されます。
|
||||
|
||||
///
|
||||
|
||||
### `RequestValidationError`のボディの使用
|
||||
|
||||
`RequestValidationError`には無効なデータを含む`body`が含まれています。
|
||||
|
||||
アプリ開発中に本体のログを取ってデバッグしたり、ユーザーに返したりなどに使用することができます。
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
|
||||
|
||||
ここで、以下のような無効な項目を送信してみてください:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"title": "towel",
|
||||
"size": "XL"
|
||||
}
|
||||
```
|
||||
|
||||
受信したボディを含むデータが無効であることを示すレスポンスが表示されます:
|
||||
|
||||
```JSON hl_lines="12 13 14 15"
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"loc": [
|
||||
"body",
|
||||
"size"
|
||||
],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"title": "towel",
|
||||
"size": "XL"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### FastAPIの`HTTPException`とStarletteの`HTTPException`
|
||||
|
||||
**FastAPI**は独自の`HTTPException`を持っています。
|
||||
|
||||
また、 **FastAPI**のエラークラス`HTTPException`はStarletteのエラークラス`HTTPException`を継承しています。
|
||||
|
||||
唯一の違いは、**FastAPI** の`HTTPException`はレスポンスに含まれるヘッダを追加できることです。
|
||||
|
||||
これはOAuth 2.0といくつかのセキュリティユーティリティのために内部的に必要とされ、使用されています。
|
||||
|
||||
そのため、コード内では通常通り **FastAPI** の`HTTPException`を発生させ続けることができます。
|
||||
|
||||
しかし、例外ハンドラを登録する際には、Starletteの`HTTPException`を登録しておく必要があります。
|
||||
|
||||
これにより、Starletteの内部コードやStarletteの拡張機能やプラグインの一部が`HTTPException`を発生させた場合、ハンドラがそれをキャッチして処理することができるようになります。
|
||||
|
||||
以下の例では、同じコード内で両方の`HTTPException`を使用できるようにするために、Starletteの例外の名前を`StarletteHTTPException`に変更しています:
|
||||
|
||||
```Python
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
```
|
||||
|
||||
### **FastAPI** の例外ハンドラの再利用
|
||||
|
||||
また、何らかの方法で例外を使用することもできますが、**FastAPI** から同じデフォルトの例外ハンドラを使用することもできます。
|
||||
|
||||
デフォルトの例外ハンドラを`fastapi.exception_handlers`からインポートして再利用することができます:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial006.py hl[2,3,4,5,15,21] *}
|
||||
|
||||
この例では、非常に表現力のあるメッセージでエラーを`print`しています。
|
||||
|
||||
しかし、例外を使用して、デフォルトの例外ハンドラを再利用することができるということが理解できます。
|
||||
@@ -0,0 +1,91 @@
|
||||
# ヘッダーのパラメータ
|
||||
|
||||
ヘッダーのパラメータは、`Query`や`Path`、`Cookie`のパラメータを定義するのと同じように定義できます。
|
||||
|
||||
## `Header`をインポート
|
||||
|
||||
まず、`Header`をインポートします:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial001.py hl[3] *}
|
||||
|
||||
## `Header`のパラメータの宣言
|
||||
|
||||
次に、`Path`や`Query`、`Cookie`と同じ構造を用いてヘッダーのパラメータを宣言します。
|
||||
|
||||
最初の値がデフォルト値で、追加の検証パラメータや注釈パラメータをすべて渡すことができます。
|
||||
|
||||
{* ../../docs_src/header_params/tutorial001.py hl[9] *}
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`Header`は`Path`や`Query`、`Cookie`の「姉妹」クラスです。また、同じ共通の`Param`クラスを継承しています。
|
||||
|
||||
しかし、`fastapi`から`Query`や`Path`、`Header`などをインポートする場合、それらは実際には特殊なクラスを返す関数であることを覚えておいてください。
|
||||
|
||||
///
|
||||
|
||||
/// info | 情報
|
||||
|
||||
ヘッダーを宣言するには、`Header`を使う必要があります。なぜなら、そうしないと、パラメータがクエリのパラメータとして解釈されてしまうからです。
|
||||
|
||||
///
|
||||
|
||||
## 自動変換
|
||||
|
||||
`Header`は`Path`や`Query`、`Cookie`が提供する機能に加え、少しだけ追加の機能を持っています。
|
||||
|
||||
ほとんどの標準ヘッダーは、「マイナス記号」(`-`)としても知られる「ハイフン」で区切られています。
|
||||
|
||||
しかし、`user-agent`のような変数はPythonでは無効です。
|
||||
|
||||
そのため、デフォルトでは、`Header`はパラメータの文字をアンダースコア(`_`)からハイフン(`-`)に変換して、ヘッダーを抽出して文書化します。
|
||||
|
||||
また、HTTPヘッダは大文字小文字を区別しないので、Pythonの標準スタイル(別名「スネークケース」)で宣言することができます。
|
||||
|
||||
そのため、`User_Agent`などのように最初の文字を大文字にする必要はなく、通常のPythonコードと同じように`user_agent`を使用することができます。
|
||||
|
||||
もしなんらかの理由でアンダースコアからハイフンへの自動変換を無効にする必要がある場合は、`Header`の`convert_underscores`に`False`を設定してください:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial002.py hl[9] *}
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
`convert_underscores`を`False`に設定する前に、HTTPプロキシやサーバの中にはアンダースコアを含むヘッダーの使用を許可していないものがあることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
## ヘッダーの重複
|
||||
|
||||
受信したヘッダーが重複することがあります。つまり、同じヘッダーで複数の値を持つということです。
|
||||
|
||||
これらの場合、リストの型宣言を使用して定義することができます。
|
||||
|
||||
重複したヘッダーのすべての値をPythonの`list`として受け取ることができます。
|
||||
|
||||
例えば、複数回出現する可能性のある`X-Token`のヘッダを定義するには、以下のように書くことができます:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial003.py hl[9] *}
|
||||
|
||||
もし、その*path operation*で通信する場合は、次のように2つのHTTPヘッダーを送信します:
|
||||
|
||||
```
|
||||
X-Token: foo
|
||||
X-Token: bar
|
||||
```
|
||||
|
||||
このレスポンスは以下のようになります:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"X-Token values": [
|
||||
"bar",
|
||||
"foo"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## まとめ
|
||||
|
||||
ヘッダーは`Header`で宣言し、`Query`や`Path`、`Cookie`と同じパターンを使用する。
|
||||
|
||||
また、変数のアンダースコアを気にする必要はありません。**FastAPI** がそれらの変換をすべて取り持ってくれます。
|
||||
@@ -0,0 +1,83 @@
|
||||
# チュートリアル - ユーザーガイド
|
||||
|
||||
このチュートリアルは**FastAPI**のほぼすべての機能の使い方を段階的に紹介します。
|
||||
|
||||
各セクションは前のセクションを踏まえた内容になっています。しかし、トピックごとに分割されているので、特定のAPIの要求を満たすようなトピックに直接たどり着けるようになっています。
|
||||
|
||||
また、将来的にリファレンスとして機能するように構築されています。
|
||||
|
||||
従って、後でこのチュートリアルに戻ってきて必要なものを確認できます。
|
||||
|
||||
## コードを実行する
|
||||
|
||||
すべてのコードブロックをコピーして直接使用できます(実際にテストされたPythonファイルです)。
|
||||
|
||||
いずれかの例を実行するには、コードを `main.py`ファイルにコピーし、` uvicorn`を次のように起動します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
<span style="color: green;">INFO</span>: Started reloader process [28720]
|
||||
<span style="color: green;">INFO</span>: Started server process [28722]
|
||||
<span style="color: green;">INFO</span>: Waiting for application startup.
|
||||
<span style="color: green;">INFO</span>: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
コードを記述またはコピーし、編集してローカルで実行することを**強くお勧めします**。
|
||||
|
||||
また、エディターで使用することで、書く必要のあるコードの少なさ、すべての型チェック、自動補完などのFastAPIの利点を実感できます。
|
||||
|
||||
---
|
||||
|
||||
## FastAPIをインストールする
|
||||
|
||||
最初のステップは、FastAPIのインストールです。
|
||||
|
||||
チュートリアルのために、すべてのオプションの依存関係と機能をインストールしたいとき:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "fastapi[all]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
...これには、コードを実行するサーバーとして使用できる `uvicorn`も含まれます。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
パーツ毎にインストールすることも可能です。
|
||||
|
||||
以下は、アプリケーションを本番環境にデプロイする際に行うであろうものです:
|
||||
|
||||
```
|
||||
pip install fastapi
|
||||
```
|
||||
|
||||
また、サーバーとして動作するように`uvicorn` をインストールします:
|
||||
|
||||
```
|
||||
pip install "uvicorn[standard]"
|
||||
```
|
||||
|
||||
そして、使用したい依存関係をそれぞれ同様にインストールします。
|
||||
|
||||
///
|
||||
|
||||
## 高度なユーザーガイド
|
||||
|
||||
**高度なユーザーガイド**もあり、**チュートリアル - ユーザーガイド**の後で読むことができます。
|
||||
|
||||
**高度なユーザーガイド**は**チュートリアル - ユーザーガイド**に基づいており、同じ概念を使用し、いくつかの追加機能を紹介しています。
|
||||
|
||||
ただし、最初に**チュートリアル - ユーザーガイド**(現在読んでいる内容)をお読みください。
|
||||
|
||||
**チュートリアル-ユーザーガイド**だけで完全なアプリケーションを構築できるように設計されています。加えて、**高度なユーザーガイド**の中からニーズに応じたアイデアを使用して、様々な拡張が可能です。
|
||||
@@ -0,0 +1,101 @@
|
||||
# メタデータとドキュメントのURL
|
||||
|
||||
**FastAPI** アプリケーションのいくつかのメタデータの設定をカスタマイズできます。
|
||||
|
||||
## タイトル、説明文、バージョン
|
||||
|
||||
以下を設定できます:
|
||||
|
||||
* **タイトル**: OpenAPIおよび自動APIドキュメントUIでAPIのタイトル/名前として使用される。
|
||||
* **説明文**: OpenAPIおよび自動APIドキュメントUIでのAPIの説明文。
|
||||
* **バージョン**: APIのバージョン。例: `v2` または `2.5.0`。
|
||||
*たとえば、以前のバージョンのアプリケーションがあり、OpenAPIも使用している場合に便利です。
|
||||
|
||||
これらを設定するには、パラメータ `title`、`description`、`version` を使用します:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001.py hl[4:6] *}
|
||||
|
||||
この設定では、自動APIドキュメントは以下の様になります:
|
||||
|
||||
<img src="/img/tutorial/metadata/image01.png">
|
||||
|
||||
## タグのためのメタデータ
|
||||
|
||||
さらに、パラメータ `openapi_tags` を使うと、path operations をグループ分けするための複数のタグに関するメタデータを追加できます。
|
||||
|
||||
それぞれのタグ毎にひとつの辞書を含むリストをとります。
|
||||
|
||||
それぞれの辞書は以下をもつことができます:
|
||||
|
||||
* `name` (**必須**): *path operations* および `APIRouter` の `tags` パラメーターで使用するのと同じタグ名である `str`。
|
||||
* `description`: タグの簡単な説明文である `str`。 Markdownで記述でき、ドキュメントUIに表示されます。
|
||||
* `externalDocs`: 外部ドキュメントを説明するための `dict`:
|
||||
* `description`: 外部ドキュメントの簡単な説明文である `str`。
|
||||
* `url` (**必須**): 外部ドキュメントのURLである `str`。
|
||||
|
||||
### タグのためのメタデータの作成
|
||||
|
||||
`users` と `items` のタグを使った例でメタデータの追加を試してみましょう。
|
||||
|
||||
タグのためのメタデータを作成し、それを `openapi_tags` パラメータに渡します。
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
|
||||
|
||||
説明文 (description) の中で Markdown を使用できることに注意してください。たとえば、「login」は太字 (**login**) で表示され、「fancy」は斜体 (_fancy_) で表示されます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
使用するすべてのタグにメタデータを追加する必要はありません。
|
||||
|
||||
///
|
||||
|
||||
### 自作タグの使用
|
||||
|
||||
`tags` パラメーターを使用して、それぞれの *path operations* (および `APIRouter`) を異なるタグに割り当てます:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
タグのより詳しい説明を知りたい場合は [Path Operation Configuration](path-operation-configuration.md#tags){.internal-link target=_blank} を参照して下さい。
|
||||
|
||||
///
|
||||
|
||||
### ドキュメントの確認
|
||||
|
||||
ここで、ドキュメントを確認すると、追加したメタデータがすべて表示されます:
|
||||
|
||||
<img src="/img/tutorial/metadata/image02.png">
|
||||
|
||||
### タグの順番
|
||||
|
||||
タグのメタデータ辞書の順序は、ドキュメントUIに表示される順序の定義にもなります。
|
||||
|
||||
たとえば、`users` はアルファベット順では `items` の後に続きます。しかし、リストの最初に `users` のメタデータ辞書を追加したため、ドキュメントUIでは `users` が先に表示されます。
|
||||
|
||||
## OpenAPI URL
|
||||
|
||||
デフォルトでは、OpenAPIスキーマは `/openapi.json` で提供されます。
|
||||
|
||||
ただし、パラメータ `openapi_url` を使用して設定を変更できます。
|
||||
|
||||
たとえば、`/api/v1/openapi.json` で提供されるように設定するには:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial002.py hl[3] *}
|
||||
|
||||
OpenAPIスキーマを完全に無効にする場合は、`openapi_url=None` を設定できます。これにより、それを使用するドキュメントUIも無効になります。
|
||||
|
||||
## ドキュメントのURL
|
||||
|
||||
以下の2つのドキュメントUIを構築できます:
|
||||
|
||||
* **Swagger UI**: `/docs` で提供されます。
|
||||
* URL はパラメータ `docs_url` で設定できます。
|
||||
* `docs_url=None` を設定することで無効にできます。
|
||||
* ReDoc: `/redoc` で提供されます。
|
||||
* URL はパラメータ `redoc_url` で設定できます。
|
||||
* `redoc_url=None` を設定することで無効にできます。
|
||||
|
||||
たとえば、`/documentation` でSwagger UIが提供されるように設定し、ReDocを無効にするには:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial003.py hl[3] *}
|
||||
@@ -0,0 +1,66 @@
|
||||
# ミドルウェア
|
||||
|
||||
**FastAPI** アプリケーションにミドルウェアを追加できます。
|
||||
|
||||
「ミドルウェア」は、すべての**リクエスト**に対して、それがあらゆる特定の*path operation*によって処理される前に機能する関数です。また、すべての**レスポンス**に対して、それを返す前に機能します。
|
||||
|
||||
* ミドルウェアはアプリケーションに届いたそれぞれの**リクエスト**を受け取ります。
|
||||
* その後、その**リクエスト**に対して何かを実行したり、必要なコードを実行したりできます。
|
||||
* 次に、アプリケーションの残りの部分に**リクエスト**を渡して (*path operation* によって) 処理させます。
|
||||
* 次に、ミドルウェアはアプリケーション (の *path operation*) によって生成された**レスポンス**を受け取ります。
|
||||
* その**レスポンス**に対して何かを実行したり、必要なコードを実行したりできます。
|
||||
* そして、**レスポンス**を返します。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`yield` を使った依存関係をもつ場合は、終了コードはミドルウェアの *後に* 実行されます。
|
||||
|
||||
バックグラウンドタスク (後述) がある場合は、それらは全てのミドルウェアの *後に* 実行されます。
|
||||
|
||||
///
|
||||
|
||||
## ミドルウェアの作成
|
||||
|
||||
ミドルウェアを作成するには、関数の上部でデコレータ `@app.middleware("http")` を使用します。
|
||||
|
||||
ミドルウェア関数は以下を受け取ります:
|
||||
|
||||
* `request`。
|
||||
* パラメータとして `request` を受け取る関数 `call_next`。
|
||||
* この関数は、対応する*path operation*に `request` を渡します。
|
||||
* 次に、対応する*path operation*によって生成された `response` を返します。
|
||||
* その後、`response` を返す前にさらに `response` を変更することもできます。
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">'X-'プレフィックスを使用</a>してカスタムの独自ヘッダーを追加できます。
|
||||
|
||||
ただし、ブラウザのクライアントに表示させたいカスタムヘッダーがある場合は、<a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">StarletteのCORSドキュメント</a>に記載されているパラメータ `expose_headers` を使用して、それらをCORS設定に追加する必要があります ([CORS (オリジン間リソース共有)](cors.md){.internal-link target=_blank})
|
||||
|
||||
///
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`from starlette.requests import Request` を使用することもできます。
|
||||
|
||||
**FastAPI**は、開発者の便利のためにこれを提供していますが、Starletteから直接きています。
|
||||
|
||||
///
|
||||
|
||||
### `response` の前後
|
||||
|
||||
*path operation* が `request` を受け取る前に、 `request` とともに実行されるコードを追加できます。
|
||||
|
||||
また `response` が生成された後、それを返す前にも追加できます。
|
||||
|
||||
例えば、リクエストの処理とレスポンスの生成にかかった秒数を含むカスタムヘッダー `X-Process-Time` を追加できます:
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
|
||||
|
||||
## その他のミドルウェア
|
||||
|
||||
他のミドルウェアの詳細については、[高度なユーザーガイド: 高度なミドルウェア](../advanced/middleware.md){.internal-link target=_blank}を参照してください。
|
||||
|
||||
次のセクションでは、ミドルウェアを使用して <abbr title="Cross-Origin Resource Sharing">CORS</abbr> を処理する方法について説明します。
|
||||
@@ -0,0 +1,97 @@
|
||||
# Path Operationの設定
|
||||
|
||||
*path operationデコレータ*を設定するためのパラメータがいくつかあります。
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
これらのパラメータは*path operation関数*ではなく、*path operationデコレータ*に直接渡されることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
## レスポンスステータスコード
|
||||
|
||||
*path operation*のレスポンスで使用する(HTTP)`status_code`を定義することができます。
|
||||
|
||||
`404`のように`int`のコードを直接渡すことができます。
|
||||
|
||||
しかし、それぞれの番号コードが何のためのものか覚えていない場合は、`status`のショートカット定数を使用することができます:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial001.py hl[3,17] *}
|
||||
|
||||
そのステータスコードはレスポンスで使用され、OpenAPIスキーマに追加されます。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
また、`from starlette import status`を使用することもできます。
|
||||
|
||||
**FastAPI** は開発者の利便性を考慮して、`fastapi.status`と同じ`starlette.status`を提供しています。しかし、これはStarletteから直接提供されています。
|
||||
|
||||
///
|
||||
|
||||
## タグ
|
||||
|
||||
`tags`パラメータを`str`の`list`(通常は1つの`str`)と一緒に渡すと、*path operation*にタグを追加できます:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002.py hl[17,22,27] *}
|
||||
|
||||
これらはOpenAPIスキーマに追加され、自動ドキュメントのインターフェースで使用されます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image01.png">
|
||||
|
||||
## 概要と説明
|
||||
|
||||
`summary`と`description`を追加できます:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003.py hl[20:21] *}
|
||||
|
||||
## docstringを用いた説明
|
||||
|
||||
説明文は長くて複数行におよぶ傾向があるので、関数<abbr title="ドキュメントに使用される関数内の最初の式(変数に代入されていない)としての複数行の文字列">docstring</abbr>内に*path operation*の説明文を宣言できます。すると、**FastAPI** は説明文を読み込んでくれます。
|
||||
|
||||
docstringに<a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a>を記述すれば、正しく解釈されて表示されます。(docstringのインデントを考慮して)
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial004.py hl[19:27] *}
|
||||
|
||||
これは対話的ドキュメントで使用されます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image02.png">
|
||||
|
||||
## レスポンスの説明
|
||||
|
||||
`response_description`パラメータでレスポンスの説明をすることができます。
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005.py hl[21] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
`respnse_description`は具体的にレスポンスを参照し、`description`は*path operation*全般を参照していることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
/// check | 確認
|
||||
|
||||
OpenAPIは*path operation*ごとにレスポンスの説明を必要としています。
|
||||
|
||||
そのため、それを提供しない場合は、**FastAPI** が自動的に「成功のレスポンス」を生成します。
|
||||
|
||||
///
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image03.png">
|
||||
|
||||
## 非推奨の*path operation*
|
||||
|
||||
*path operation*を<abbr title="非推奨、使わない方がよい">deprecated</abbr>としてマークする必要があるが、それを削除しない場合は、`deprecated`パラメータを渡します:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
|
||||
|
||||
対話的ドキュメントでは非推奨と明記されます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image04.png">
|
||||
|
||||
*path operations*が非推奨である場合とそうでない場合でどのように見えるかを確認してください:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image05.png">
|
||||
|
||||
## まとめ
|
||||
|
||||
*path operationデコレータ*にパラメータを渡すことで、*path operations*のメタデータを簡単に設定・追加することができます。
|
||||
@@ -0,0 +1,117 @@
|
||||
# パスパラメータと数値の検証
|
||||
|
||||
クエリパラメータに対して`Query`でより多くのバリデーションとメタデータを宣言できるのと同じように、パスパラメータに対しても`Path`で同じ種類のバリデーションとメタデータを宣言することができます。
|
||||
|
||||
## Pathのインポート
|
||||
|
||||
まず初めに、`fastapi`から`Path`をインポートします:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[1] *}
|
||||
|
||||
## メタデータの宣言
|
||||
|
||||
パラメータは`Query`と同じものを宣言することができます。
|
||||
|
||||
例えば、パスパラメータ`item_id`に対して`title`のメタデータを宣言するには以下のようにします:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[8] *}
|
||||
|
||||
/// note | 備考
|
||||
|
||||
パスの一部でなければならないので、パスパラメータは常に必須です。
|
||||
|
||||
そのため、`...`を使用して必須と示す必要があります。
|
||||
|
||||
それでも、`None`で宣言しても、デフォルト値を設定しても、何の影響もなく、常に必要とされていることに変わりはありません。
|
||||
|
||||
///
|
||||
|
||||
## 必要に応じてパラメータを並び替える
|
||||
|
||||
クエリパラメータ`q`を必須の`str`として宣言したいとしましょう。
|
||||
|
||||
また、このパラメータには何も宣言する必要がないので、`Query`を使う必要はありません。
|
||||
|
||||
しかし、パスパラメータ`item_id`のために`Path`を使用する必要があります。
|
||||
|
||||
Pythonは「デフォルト」を持たない値の前に「デフォルト」を持つ値を置くことができません。
|
||||
|
||||
しかし、それらを並び替えることができ、デフォルト値を持たない値(クエリパラメータ`q`)を最初に持つことができます。
|
||||
|
||||
**FastAPI**では関係ありません。パラメータは名前、型、デフォルトの宣言(`Query`、`Path`など)で検出され、順番は気にしません。
|
||||
|
||||
そのため、以下のように関数を宣言することができます:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[8] *}
|
||||
|
||||
## 必要に応じてパラメータを並び替えるトリック
|
||||
|
||||
クエリパラメータ`q`を`Query`やデフォルト値なしで宣言し、パスパラメータ`item_id`を`Path`を用いて宣言し、それらを別の順番に並びたい場合、Pythonには少し特殊な構文が用意されています。
|
||||
|
||||
関数の最初のパラメータとして`*`を渡します。
|
||||
|
||||
Pythonはその`*`で何かをすることはありませんが、それ以降のすべてのパラメータがキーワード引数(キーと値のペア)として呼ばれるべきものであると知っているでしょう。それは<abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>としても知られています。たとえデフォルト値がなくても。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[8] *}
|
||||
|
||||
## 数値の検証: 以上
|
||||
|
||||
`Query`と`Path`(、そして後述する他のもの)を用いて、文字列の制約を宣言することができますが、数値の制約も同様に宣言できます。
|
||||
|
||||
ここで、`ge=1`の場合、`item_id`は`1`「より大きい`g`か、同じ`e`」整数でなれけばなりません。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004.py hl[8] *}
|
||||
|
||||
## 数値の検証: より大きいと小なりイコール
|
||||
|
||||
以下も同様です:
|
||||
|
||||
* `gt`: より大きい(`g`reater `t`han)
|
||||
* `le`: 小なりイコール(`l`ess than or `e`qual)
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005.py hl[9] *}
|
||||
|
||||
## 数値の検証: 浮動小数点、 大なり小なり
|
||||
|
||||
数値のバリデーションは`float`の値に対しても有効です。
|
||||
|
||||
ここで重要になってくるのは<abbr title="より大きい"><code>gt</code></abbr>だけでなく<abbr title="以下"><code>ge</code></abbr>も宣言できることです。これと同様に、例えば、値が`1`より小さくても`0`より大きくなければならないことを要求することができます。
|
||||
|
||||
したがって、`0.5`は有効な値ですが、`0.0`や`0`はそうではありません。
|
||||
|
||||
これは<abbr title="未満"><code>lt</code></abbr>も同じです。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006.py hl[11] *}
|
||||
|
||||
## まとめ
|
||||
|
||||
`Query`と`Path`(そしてまだ見たことない他のもの)では、[クエリパラメータと文字列の検証](query-params-str-validations.md){.internal-link target=_blank}と同じようにメタデータと文字列の検証を宣言することができます。
|
||||
|
||||
また、数値のバリデーションを宣言することもできます:
|
||||
|
||||
* `gt`: より大きい(`g`reater `t`han)
|
||||
* `ge`: 以上(`g`reater than or `e`qual)
|
||||
* `lt`: より小さい(`l`ess `t`han)
|
||||
* `le`: 以下(`l`ess than or `e`qual)
|
||||
|
||||
/// info | 情報
|
||||
|
||||
`Query`、`Path`などは後に共通の`Param`クラスのサブクラスを見ることになります。(使う必要はありません)
|
||||
|
||||
そして、それらすべては、これまで見てきた追加のバリデーションとメタデータと同じパラメータを共有しています。
|
||||
|
||||
///
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`fastapi`から`Query`、`Path`などをインポートすると、これらは実際には関数です。
|
||||
|
||||
呼び出されると、同じ名前のクラスのインスタンスを返します。
|
||||
|
||||
そのため、関数である`Query`をインポートし、それを呼び出すと、`Query`という名前のクラスのインスタンスが返されます。
|
||||
|
||||
これらの関数は(クラスを直接使うのではなく)エディタが型についてエラーとしないようにするために存在します。
|
||||
|
||||
この方法によって、これらのエラーを無視するための設定を追加することなく、通常のエディタやコーディングツールを使用することができます。
|
||||
|
||||
///
|
||||
@@ -0,0 +1,250 @@
|
||||
# パスパラメータ
|
||||
|
||||
Pythonのformat文字列と同様のシンタックスで「パスパラメータ」や「パス変数」を宣言できます:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial001.py hl[6,7] *}
|
||||
|
||||
パスパラメータ `item_id` の値は、引数 `item_id` として関数に渡されます。
|
||||
|
||||
しがたって、この例を実行して <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a> にアクセスすると、次のレスポンスが表示されます。
|
||||
|
||||
```JSON
|
||||
{"item_id":"foo"}
|
||||
```
|
||||
|
||||
## パスパラメータと型
|
||||
|
||||
標準のPythonの型アノテーションを使用して、関数内のパスパラメータの型を宣言できます:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial002.py hl[7] *}
|
||||
|
||||
ここでは、 `item_id` は `int` として宣言されています。
|
||||
|
||||
/// check | 確認
|
||||
|
||||
これにより、関数内でのエディターサポート (エラーチェックや補完など) が提供されます。
|
||||
|
||||
///
|
||||
|
||||
## データ<abbr title="別名: serialization, parsing, marshalling">変換</abbr>
|
||||
|
||||
この例を実行し、ブラウザで <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a> を開くと、次のレスポンスが表示されます:
|
||||
|
||||
```JSON
|
||||
{"item_id":3}
|
||||
```
|
||||
|
||||
/// check | 確認
|
||||
|
||||
関数が受け取った(および返した)値は、文字列の `"3"` ではなく、Pythonの `int` としての `3` であることに注意してください。
|
||||
|
||||
したがって、型宣言を使用すると、**FastAPI**は自動リクエスト <abbr title="HTTPリクエストで受け取った文字列をPythonデータへ変換する">"解析"</abbr> を行います。
|
||||
|
||||
///
|
||||
|
||||
## データバリデーション
|
||||
|
||||
しかしブラウザで <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a> を開くと、次のHTTPエラーが表示されます:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"loc": [
|
||||
"path",
|
||||
"item_id"
|
||||
],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
これは、パスパラメータ `item_id` が `int` ではない値 `"foo"` だからです。
|
||||
|
||||
<a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a> で見られるように、intのかわりに `float` が与えられた場合にも同様なエラーが表示されます。
|
||||
|
||||
/// check | 確認
|
||||
|
||||
したがって、Pythonの型宣言を使用することで、**FastAPI**はデータのバリデーションを行います。
|
||||
|
||||
表示されたエラーには問題のある箇所が明確に指摘されていることに注意してください。
|
||||
|
||||
これは、APIに関連するコードの開発およびデバッグに非常に役立ちます。
|
||||
|
||||
///
|
||||
|
||||
## ドキュメント
|
||||
|
||||
そしてブラウザで <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> を開くと、以下の様な自動的に生成された対話的なドキュメントが表示されます。
|
||||
|
||||
<img src="/img/tutorial/path-params/image01.png">
|
||||
|
||||
/// check | 確認
|
||||
|
||||
繰り返しになりますが、Python型宣言を使用するだけで、**FastAPI**は対話的なAPIドキュメントを自動的に生成します(Swagger UIを統合)。
|
||||
|
||||
パスパラメータが整数として宣言されていることに注意してください。
|
||||
|
||||
///
|
||||
|
||||
## 標準であることのメリット、ドキュメンテーションの代替物
|
||||
|
||||
また、生成されたスキーマが <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a> 標準に従っているので、互換性のあるツールが多数あります。
|
||||
|
||||
このため、**FastAPI**自体が代替のAPIドキュメントを提供します(ReDocを使用)。これは、 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> にアクセスすると確認できます。
|
||||
|
||||
<img src="/img/tutorial/path-params/image02.png">
|
||||
|
||||
同様に、互換性のあるツールが多数あります(多くの言語用のコード生成ツールを含む)。
|
||||
|
||||
## Pydantic
|
||||
|
||||
すべてのデータバリデーションは <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> によって内部で実行されるため、Pydanticの全てのメリットが得られます。そして、安心して利用することができます。
|
||||
|
||||
`str`、 `float` 、 `bool` および他の多くの複雑なデータ型を型宣言に使用できます。
|
||||
|
||||
これらのいくつかについては、チュートリアルの次の章で説明します。
|
||||
|
||||
## 順序の問題
|
||||
|
||||
*path operations* を作成する際、固定パスをもつ状況があり得ます。
|
||||
|
||||
`/users/me` から、現在のユーザに関するデータを取得するとします。
|
||||
|
||||
さらに、ユーザIDによって特定のユーザに関する情報を取得するパス `/users/{user_id}` ももつことができます。
|
||||
|
||||
*path operations* は順に評価されるので、 `/users/me` が `/users/{user_id}` よりも先に宣言されているか確認する必要があります:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
|
||||
|
||||
それ以外の場合、 `/users/{users_id}` は `/users/me` としてもマッチします。値が「"me"」であるパラメータ `user_id` を受け取ると「考え」ます。
|
||||
|
||||
## 定義済みの値
|
||||
|
||||
*パスパラメータ*を受け取る *path operation* をもち、有効な*パスパラメータ*の値を事前に定義したい場合は、標準のPython <abbr title="Enumeration">`Enum`</abbr> を利用できます。
|
||||
|
||||
### `Enum` クラスの作成
|
||||
|
||||
`Enum` をインポートし、 `str` と `Enum` を継承したサブクラスを作成します。
|
||||
|
||||
`str` を継承することで、APIドキュメントは値が `文字列` でなければいけないことを知り、正確にレンダリングできるようになります。
|
||||
|
||||
そして、固定値のクラス属性を作ります。すると、その値が使用可能な値となります:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[1,6,7,8,9] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Enumerations (もしくは、enums)はPython 3.4以降で利用できます</a>。
|
||||
|
||||
///
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
"AlexNet"、"ResNet"そして"LeNet"は機械学習<abbr title="Technically, Deep Learning model architectures">モデル</abbr>の名前です。
|
||||
|
||||
///
|
||||
|
||||
### *パスパラメータ*の宣言
|
||||
|
||||
次に、作成したenumクラスである`ModelName`を使用した型アノテーションをもつ*パスパラメータ*を作成します:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[16] *}
|
||||
|
||||
### ドキュメントの確認
|
||||
|
||||
*パスパラメータ*の利用可能な値が事前に定義されているので、対話的なドキュメントで適切に表示できます:
|
||||
|
||||
<img src="/img/tutorial/path-params/image03.png">
|
||||
|
||||
### Python*列挙型*の利用
|
||||
|
||||
*パスパラメータ*の値は*列挙型メンバ*となります。
|
||||
|
||||
#### *列挙型メンバ*の比較
|
||||
|
||||
これは、作成した列挙型 `ModelName` の*列挙型メンバ*と比較できます:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
|
||||
|
||||
#### *列挙値*の取得
|
||||
|
||||
`model_name.value` 、もしくは一般に、 `your_enum_member.value` を使用して実際の値 (この場合は `str`) を取得できます。
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[20] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`ModelName.lenet.value` でも `"lenet"` 値にアクセスできます。
|
||||
|
||||
///
|
||||
|
||||
#### *列挙型メンバ*の返却
|
||||
|
||||
*path operation* から*列挙型メンバ*を返すことができます。JSONボディ(`dict` など)でネストすることもできます。
|
||||
|
||||
それらはクライアントに返される前に適切な値 (この場合は文字列) に変換されます。
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
|
||||
|
||||
クライアントは以下の様なJSONレスポンスを得ます:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"model_name": "alexnet",
|
||||
"message": "Deep Learning FTW!"
|
||||
}
|
||||
```
|
||||
|
||||
## パスを含んだパスパラメータ
|
||||
|
||||
パス `/files/{file_path}` となる *path operation* を持っているとしましょう。
|
||||
|
||||
ただし、 `home/johndoe/myfile.txt` のような*パス*を含んだ `file_path` が必要です。
|
||||
|
||||
したがって、URLは `/files/home/johndoe/myfile.txt` の様になります。
|
||||
|
||||
### OpenAPIサポート
|
||||
|
||||
OpenAPIはテストや定義が困難なシナリオにつながる可能性があるため、内部に*パス*を含む*パスパラメータ*の宣言をサポートしていません。
|
||||
|
||||
それにも関わらず、Starletteの内部ツールのひとつを使用することで、**FastAPI**はそれが実現できます。
|
||||
|
||||
そして、パラメータがパスを含むべきであることを明示的にドキュメントに追加することなく、機能します。
|
||||
|
||||
### パス変換
|
||||
|
||||
Starletteのオプションを直接使用することで、以下のURLの様な*パス*を含んだ、*パスパラメータ*の宣言ができます:
|
||||
|
||||
```
|
||||
/files/{file_path:path}
|
||||
```
|
||||
|
||||
この場合、パラメータ名は `file_path` です。そして、最後の部分 `:path` はパラメータがいかなる*パス*にもマッチすることを示します。
|
||||
|
||||
したがって、以下の様に使用できます:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial004.py hl[6] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
最初のスラッシュ (`/`)が付いている `/home/johndoe/myfile.txt` をパラメータが含んでいる必要があります。
|
||||
|
||||
この場合、URLは `files` と `home` の間にダブルスラッシュ (`//`) のある、 `/files//home/johndoe/myfile.txt` になります。
|
||||
|
||||
///
|
||||
|
||||
## まとめ
|
||||
|
||||
簡潔で、本質的で、標準的なPythonの型宣言を使用することで、**FastAPI**は以下を行います:
|
||||
|
||||
* エディターサポート: エラーチェック、自動補完、など
|
||||
* データ「<abbr title="HTTPリクエストで受け取った文字列をPythonデータへ変換する">解析</abbr>」
|
||||
* データバリデーション
|
||||
* APIアノテーションと自動ドキュメント生成
|
||||
|
||||
そしてこれはたった一度宣言するだけです。
|
||||
|
||||
これは恐らく、(パフォーマンスを除いて) 他のフレームワークと比較したときの、**FastAPI**の主な目に見える利点です。
|
||||
@@ -0,0 +1,68 @@
|
||||
# クエリパラメータモデル
|
||||
|
||||
もし関連する**複数のクエリパラメータ**から成るグループがあるなら、それらを宣言するために、**Pydanticモデル**を作成できます。
|
||||
|
||||
こうすることで、**複数の場所**で**そのPydanticモデルを再利用**でき、バリデーションやメタデータを、すべてのクエリパラメータに対して一度に宣言できます。😎
|
||||
|
||||
/// note | 備考
|
||||
|
||||
この機能は、FastAPIのバージョン `0.115.0` からサポートされています。🤓
|
||||
|
||||
///
|
||||
|
||||
## クエリパラメータにPydanticモデルを使用する
|
||||
|
||||
必要な**複数のクエリパラメータ**を**Pydanticモデル**で宣言し、さらに、それを `Query` として宣言しましょう:
|
||||
|
||||
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
|
||||
|
||||
**FastAPI**は、リクエストの**クエリパラメータ**からそれぞれの**フィールド**のデータを**抽出**し、定義された**Pydanticモデル**を提供します。
|
||||
|
||||
## ドキュメントの確認
|
||||
|
||||
対話的APIドキュメント `/docs` でクエリパラメータを確認できます:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/query-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
## 余分なクエリパラメータを禁止する
|
||||
|
||||
特定の(あまり一般的ではないかもしれない)ケースで、受け付けるクエリパラメータを**制限**する必要があるかもしれません。
|
||||
|
||||
Pydanticのモデルの Configuration を利用して、 `extra` フィールドを `forbid` とすることができます。
|
||||
|
||||
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
|
||||
|
||||
もしクライアントが**クエリパラメータ**として**余分な**データを送ろうとすると、**エラー**レスポンスが返されます。
|
||||
|
||||
例えば、クライアントがクエリパラメータ `tool` に、値 `plumbus` を設定して送ろうとすると:
|
||||
|
||||
```http
|
||||
https://example.com/items/?limit=10&tool=plumbus
|
||||
```
|
||||
|
||||
クエリパラメータ `tool` が許可されていないことを通知する**エラー**レスポンスが返されます。
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["query", "tool"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "plumbus"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## まとめ
|
||||
|
||||
**FastAPI**では、**クエリパラメータ**を宣言するために、**Pydanticモデル**を使用できます。😎
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
ネタバレ注意: Pydanticモデルはクッキーやヘッダーの宣言にも使用できますが、その内容については後のチュートリアルで学びます。🤫
|
||||
|
||||
///
|
||||
@@ -0,0 +1,296 @@
|
||||
# クエリパラメータと文字列の検証
|
||||
|
||||
**FastAPI** ではパラメータの追加情報とバリデーションを宣言することができます。
|
||||
|
||||
以下のアプリケーションを例にしてみましょう:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial001.py hl[9] *}
|
||||
|
||||
クエリパラメータ `q` は `Optional[str]` 型で、`None` を許容する `str` 型を意味しており、デフォルトは `None` です。そのため、FastAPIはそれが必須ではないと理解します。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
FastAPIは、 `q` はデフォルト値が `=None` であるため、必須ではないと理解します。
|
||||
|
||||
`Optional[str]` における `Optional` はFastAPIには利用されませんが、エディターによるより良いサポートとエラー検出を可能にします。
|
||||
|
||||
///
|
||||
|
||||
## バリデーションの追加
|
||||
|
||||
`q`はオプショナルですが、もし値が渡されてきた場合には、**50文字を超えないこと**を強制してみましょう。
|
||||
|
||||
### `Query`のインポート
|
||||
|
||||
そのために、まずは`fastapi`から`Query`をインポートします:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002.py hl[3] *}
|
||||
|
||||
## デフォルト値として`Query`を使用
|
||||
|
||||
パラメータのデフォルト値として使用し、パラメータ`max_length`を50に設定します:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002.py hl[9] *}
|
||||
|
||||
デフォルト値`None`を`Query(default=None)`に置き換える必要があるので、`Query`の最初の引数はデフォルト値を定義するのと同じです。
|
||||
|
||||
なので:
|
||||
|
||||
```Python
|
||||
q: Optional[str] = Query(default=None)
|
||||
```
|
||||
|
||||
...を以下と同じようにパラメータをオプションにします:
|
||||
|
||||
```Python
|
||||
q: Optional[str] = None
|
||||
```
|
||||
|
||||
しかし、これはクエリパラメータとして明示的に宣言しています。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
FastAPIは以下の部分を気にすることを覚えておいてください:
|
||||
|
||||
```Python
|
||||
= None
|
||||
```
|
||||
|
||||
もしくは:
|
||||
|
||||
```Python
|
||||
= Query(default=None)
|
||||
```
|
||||
|
||||
そして、 `None` を利用することでクエリパラメータが必須ではないと検知します。
|
||||
|
||||
`Optional` の部分は、エディターによるより良いサポートを可能にします。
|
||||
|
||||
///
|
||||
|
||||
そして、さらに多くのパラメータを`Query`に渡すことができます。この場合、文字列に適用される、`max_length`パラメータを指定します。
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None, max_length=50)
|
||||
```
|
||||
|
||||
これにより、データを検証し、データが有効でない場合は明確なエラーを表示し、OpenAPIスキーマの *path operation* にパラメータを記載します。
|
||||
|
||||
## バリデーションをさらに追加する
|
||||
|
||||
パラメータ`min_length`も追加することができます:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial003.py hl[10] *}
|
||||
|
||||
## 正規表現の追加
|
||||
|
||||
パラメータが一致するべき<abbr title="正規表現とは、文字列の検索パターンを定義する文字列です。">正規表現</abbr>を定義することができます:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004.py hl[11] *}
|
||||
|
||||
この特定の正規表現は受け取ったパラメータの値をチェックします:
|
||||
|
||||
* `^`: は、これ以降の文字で始まり、これより以前には文字はありません。
|
||||
* `fixedquery`: は、正確な`fixedquery`を持っています.
|
||||
* `$`: で終わる場合、`fixedquery`以降には文字はありません.
|
||||
|
||||
もしこれらすべての **正規表現**のアイデアについて迷っていても、心配しないでください。多くの人にとって難しい話題です。正規表現を必要としなくても、まだ、多くのことができます。
|
||||
|
||||
しかし、あなたがそれらを必要とし、学ぶときにはすでに、 **FastAPI**で直接それらを使用することができます。
|
||||
|
||||
## デフォルト値
|
||||
|
||||
第一引数に`None`を渡して、デフォルト値として使用するのと同じように、他の値を渡すこともできます。
|
||||
|
||||
クエリパラメータ`q`の`min_length`を`3`とし、デフォルト値を`fixedquery`としてみましょう:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005.py hl[7] *}
|
||||
|
||||
/// note | 備考
|
||||
|
||||
デフォルト値を指定すると、パラメータは任意になります。
|
||||
|
||||
///
|
||||
|
||||
## 必須にする
|
||||
|
||||
これ以上、バリデーションやメタデータを宣言する必要のない場合は、デフォルト値を指定しないだけでクエリパラメータ`q`を必須にすることができます。以下のように:
|
||||
|
||||
```Python
|
||||
q: str
|
||||
```
|
||||
|
||||
以下の代わりに:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
現在は以下の例のように`Query`で宣言しています:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None, min_length=3)
|
||||
```
|
||||
|
||||
そのため、`Query`を使用して必須の値を宣言する必要がある場合は、第一引数に`...`を使用することができます:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006.py hl[7] *}
|
||||
|
||||
/// info | 情報
|
||||
|
||||
これまで`...`を見たことがない方へ: これは特殊な単一値です。<a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">Pythonの一部であり、"Ellipsis"と呼ばれています</a>。
|
||||
|
||||
///
|
||||
|
||||
これは **FastAPI** にこのパラメータが必須であることを知らせます。
|
||||
|
||||
## クエリパラメータのリスト / 複数の値
|
||||
|
||||
クエリパラメータを明示的に`Query`で宣言した場合、値のリストを受け取るように宣言したり、複数の値を受け取るように宣言したりすることもできます。
|
||||
|
||||
例えば、URL内に複数回出現するクエリパラメータ`q`を宣言するには以下のように書きます:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial011.py hl[9] *}
|
||||
|
||||
そしてURLは以下です:
|
||||
|
||||
```
|
||||
http://localhost:8000/items/?q=foo&q=bar
|
||||
```
|
||||
|
||||
複数の*クエリパラメータ*の値`q`(`foo`と`bar`)を*path operation関数*内で*関数パラメータ*`q`としてPythonの`list`を受け取ることになります。
|
||||
|
||||
そのため、このURLのレスポンスは以下のようになります:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"q": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
上述の例のように、`list`型のクエリパラメータを宣言するには明示的に`Query`を使用する必要があります。そうしない場合、リクエストボディと解釈されます。
|
||||
|
||||
///
|
||||
|
||||
対話的APIドキュメントは複数の値を許可するために自動的に更新されます。
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/query-params-str-validations/image02.png">
|
||||
|
||||
### デフォルト値を持つ、クエリパラメータのリスト / 複数の値
|
||||
|
||||
また、値が指定されていない場合はデフォルトの`list`を定義することもできます。
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012.py hl[9] *}
|
||||
|
||||
以下のURLを開くと:
|
||||
|
||||
```
|
||||
http://localhost:8000/items/
|
||||
```
|
||||
|
||||
`q`のデフォルトは: `["foo", "bar"]` となり、レスポンスは以下のようになります:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"q": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### `list`を使う
|
||||
|
||||
`List[str]`の代わりに直接`list`を使うこともできます:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013.py hl[7] *}
|
||||
|
||||
/// note | 備考
|
||||
|
||||
この場合、FastAPIはリストの内容をチェックしないことを覚えておいてください。
|
||||
|
||||
例えば`List[int]`はリストの内容が整数であるかどうかをチェックします(そして、文書化します)。しかし`list`だけではそうしません。
|
||||
|
||||
///
|
||||
|
||||
## より多くのメタデータを宣言する
|
||||
|
||||
パラメータに関する情報をさらに追加することができます。
|
||||
|
||||
その情報は、生成されたOpenAPIに含まれ、ドキュメントのユーザーインターフェースや外部のツールで使用されます。
|
||||
|
||||
/// note | 備考
|
||||
|
||||
ツールによってOpenAPIのサポートのレベルが異なる可能性があることを覚えておいてください。
|
||||
|
||||
その中には、宣言されたすべての追加情報が表示されていないものもあるかもしれませんが、ほとんどの場合、不足している機能はすでに開発の計画がされています。
|
||||
|
||||
///
|
||||
|
||||
`title`を追加できます:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial007.py hl[9] *}
|
||||
|
||||
`description`を追加できます:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial008.py hl[13] *}
|
||||
|
||||
## エイリアスパラメータ
|
||||
|
||||
パラメータに`item-query`を指定するとします.
|
||||
|
||||
以下のような感じです:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
```
|
||||
|
||||
しかし、`item-query`は有効なPythonの変数名ではありません。
|
||||
|
||||
最も近いのは`item_query`でしょう。
|
||||
|
||||
しかし、どうしても`item-query`と正確に一致している必要があるとします...
|
||||
|
||||
それならば、`alias`を宣言することができます。エイリアスはパラメータの値を見つけるのに使用されます:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial009.py hl[9] *}
|
||||
|
||||
## 非推奨パラメータ
|
||||
|
||||
さて、このパラメータが気に入らなくなったとしましょう
|
||||
|
||||
それを使っているクライアントがいるので、しばらくは残しておく必要がありますが、ドキュメントには<abbr title="使わない方がよい">非推奨</abbr>と明記しておきたいです。
|
||||
|
||||
その場合、`Query`にパラメータ`deprecated=True`を渡します:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial010.py hl[18] *}
|
||||
|
||||
ドキュメントは以下のようになります:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/query-params-str-validations/image01.png">
|
||||
|
||||
## まとめ
|
||||
|
||||
パラメータに追加のバリデーションとメタデータを宣言することができます。
|
||||
|
||||
一般的なバリデーションとメタデータ:
|
||||
|
||||
* `alias`
|
||||
* `title`
|
||||
* `description`
|
||||
* `deprecated`
|
||||
|
||||
文字列のためのバリデーション:
|
||||
|
||||
* `min_length`
|
||||
* `max_length`
|
||||
* `regex`
|
||||
|
||||
この例では、`str`の値のバリデーションを宣言する方法を見てきました。
|
||||
|
||||
数値のような他の型のバリデーションを宣言する方法は次の章を参照してください。
|
||||
@@ -0,0 +1,186 @@
|
||||
# クエリパラメータ
|
||||
|
||||
パスパラメータではない関数パラメータを宣言すると、それらは自動的に "クエリ" パラメータとして解釈されます。
|
||||
|
||||
{* ../../docs_src/query_params/tutorial001.py hl[9] *}
|
||||
|
||||
クエリはURL内で `?` の後に続くキーとバリューの組で、 `&` で区切られています。
|
||||
|
||||
例えば、以下の様なURL内で:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?skip=0&limit=10
|
||||
```
|
||||
|
||||
...クエリパラメータは:
|
||||
|
||||
* `skip`: 値は `0`
|
||||
* `limit`: 値は `10`
|
||||
|
||||
これらはURLの一部なので、「自然に」文字列になります。
|
||||
|
||||
しかしPythonの型を宣言すると (上記の例では `int` として)、その型に変換されバリデーションが行われます。
|
||||
|
||||
パスパラメータに適用される処理と完全に同様な処理がクエリパラメータにも施されます:
|
||||
|
||||
* エディターサポート (明らかに)
|
||||
* データ「<abbr title="HTTPリクエストで受け取った文字列をPythonデータへ変換する">解析</abbr>」
|
||||
* データバリデーション
|
||||
* 自動ドキュメント生成
|
||||
|
||||
## デフォルト
|
||||
|
||||
クエリパラメータはパスの固定部分ではないので、オプショナルとしたり、デフォルト値をもつことができます。
|
||||
|
||||
上述の例では、`skip=0` と `limit=10` というデフォルト値を持っています。
|
||||
|
||||
したがって、以下のURLにアクセスすることは:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/
|
||||
```
|
||||
|
||||
以下のURLにアクセスすることと同等になります:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?skip=0&limit=10
|
||||
```
|
||||
|
||||
しかし、例えば、以下にアクセスすると:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?skip=20
|
||||
```
|
||||
|
||||
関数内のパラメータの値は以下の様になります:
|
||||
|
||||
* `skip=20`: URL内にセットしたため
|
||||
* `limit=10`: デフォルト値
|
||||
|
||||
## オプショナルなパラメータ
|
||||
|
||||
同様に、デフォルト値を `None` とすることで、オプショナルなクエリパラメータを宣言できます:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial002.py hl[9] *}
|
||||
|
||||
この場合、関数パラメータ `q` はオプショナルとなり、デフォルトでは `None` になります。
|
||||
|
||||
/// check | 確認
|
||||
|
||||
パスパラメータ `item_id` はパスパラメータであり、`q` はそれとは違ってクエリパラメータであると判別できるほど**FastAPI** が賢いということにも注意してください。
|
||||
|
||||
///
|
||||
|
||||
## クエリパラメータの型変換
|
||||
|
||||
`bool` 型も宣言できます。これは以下の様に変換されます:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial003.py hl[9] *}
|
||||
|
||||
この場合、以下にアクセスすると:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=1
|
||||
```
|
||||
|
||||
もしくは、
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=True
|
||||
```
|
||||
|
||||
もしくは、
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=true
|
||||
```
|
||||
|
||||
もしくは、
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=on
|
||||
```
|
||||
|
||||
もしくは、
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=yes
|
||||
```
|
||||
|
||||
もしくは、他の大文字小文字のバリエーション (アッパーケース、最初の文字だけアッパーケース、など)で、関数は `short` パラメータを `True` な `bool` 値として扱います。それ以外は `False` になります。
|
||||
|
||||
## 複数のパスパラメータとクエリパラメータ
|
||||
|
||||
複数のパスパラメータとクエリパラメータを同時に宣言できます。**FastAPI**は互いを区別できます。
|
||||
|
||||
そして特定の順序で宣言しなくてもよいです。
|
||||
|
||||
名前で判別されます:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial004.py hl[8,10] *}
|
||||
|
||||
## 必須のクエリパラメータ
|
||||
|
||||
パスパラメータ以外のパラメータ (今のところ、クエリパラメータのみ説明しました) のデフォルト値を宣言した場合、そのパラメータは必須ではなくなります。
|
||||
|
||||
特定の値を与えずにただオプショナルにしたい場合はデフォルト値を `None` にして下さい。
|
||||
|
||||
しかしクエリパラメータを必須にしたい場合は、ただデフォルト値を宣言しなければよいです:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
|
||||
|
||||
ここで、クエリパラメータ `needy` は `str` 型の必須のクエリパラメータです
|
||||
|
||||
以下のURLをブラウザで開くと:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo-item
|
||||
```
|
||||
|
||||
...必須のパラメータ `needy` を加えなかったので、以下の様なエラーが表示されます:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"loc": [
|
||||
"query",
|
||||
"needy"
|
||||
],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`needy` は必須のパラメータなので、URLにセットする必要があります:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
|
||||
```
|
||||
|
||||
...これはうまくいくでしょう:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item_id": "foo-item",
|
||||
"needy": "sooooneedy"
|
||||
}
|
||||
```
|
||||
|
||||
そして当然、あるパラメータを必須に、別のパラメータにデフォルト値を設定し、また別のパラメータをオプショナルにできます:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial006.py hl[10] *}
|
||||
|
||||
この場合、3つのクエリパラメータがあります。:
|
||||
|
||||
* `needy`、必須の `str` 。
|
||||
* `skip`、デフォルト値を `0` とする `int` 。
|
||||
* `limit`、オプショナルな `int` 。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
[パスパラメータ](path-params.md#_8){.internal-link target=_blank}と同様に `Enum` を使用できます。
|
||||
|
||||
///
|
||||
@@ -0,0 +1,37 @@
|
||||
# リクエストフォームとファイル
|
||||
|
||||
`File`と`Form`を同時に使うことでファイルとフォームフィールドを定義することができます。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
アップロードされたファイルやフォームデータを受信するには、まず<a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>をインストールします。
|
||||
|
||||
例えば、`pip install python-multipart`のように。
|
||||
|
||||
///
|
||||
|
||||
## `File`と`Form`のインポート
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001.py hl[1] *}
|
||||
|
||||
## `File`と`Form`のパラメータの定義
|
||||
|
||||
ファイルやフォームのパラメータは`Body`や`Query`の場合と同じように作成します:
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001.py hl[8] *}
|
||||
|
||||
ファイルとフォームフィールドがフォームデータとしてアップロードされ、ファイルとフォームフィールドを受け取ります。
|
||||
|
||||
また、いくつかのファイルを`bytes`として、いくつかのファイルを`UploadFile`として宣言することができます。
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
*path operation*で複数の`File`と`Form`パラメータを宣言することができますが、JSONとして受け取ることを期待している`Body`フィールドを宣言することはできません。なぜなら、リクエストのボディは`application/json`の代わりに`multipart/form-data`を使ってエンコードされているからです。
|
||||
|
||||
これは **FastAPI** の制限ではなく、HTTPプロトコルの一部です。
|
||||
|
||||
///
|
||||
|
||||
## まとめ
|
||||
|
||||
同じリクエストでデータやファイルを受け取る必要がある場合は、`File` と`Form`を一緒に使用します。
|
||||
@@ -0,0 +1,69 @@
|
||||
# フォームデータ
|
||||
|
||||
JSONの代わりにフィールドを受け取る場合は、`Form`を使用します。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
フォームを使うためには、まず<a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>をインストールします。
|
||||
|
||||
たとえば、`pip install python-multipart`のように。
|
||||
|
||||
///
|
||||
|
||||
## `Form`のインポート
|
||||
|
||||
`fastapi`から`Form`をインポートします:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001.py hl[1] *}
|
||||
|
||||
## `Form`のパラメータの定義
|
||||
|
||||
`Body`や`Query`の場合と同じようにフォームパラメータを作成します:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001.py hl[7] *}
|
||||
|
||||
例えば、OAuth2仕様が使用できる方法の1つ(「パスワードフロー」と呼ばれる)では、フォームフィールドとして`username`と`password`を送信する必要があります。
|
||||
|
||||
<abbr title="仕様">仕様</abbr>では、フィールドの名前が`username`と`password`であることと、JSONではなくフォームフィールドとして送信されることを要求しています。
|
||||
|
||||
`Form`では`Body`(および`Query`や`Path`、`Cookie`)と同じメタデータとバリデーションを宣言することができます。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
`Form`は`Body`を直接継承するクラスです。
|
||||
|
||||
///
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
フォームのボディを宣言するには、明示的に`Form`を使用する必要があります。なぜなら、これを使わないと、パラメータはクエリパラメータやボディ(JSON)パラメータとして解釈されるからです。
|
||||
|
||||
///
|
||||
|
||||
## 「フォームフィールド」について
|
||||
|
||||
HTMLフォーム(`<form></form>`)がサーバにデータを送信する方法は、通常、そのデータに「特別な」エンコーディングを使用していますが、これはJSONとは異なります。
|
||||
|
||||
**FastAPI** は、JSONの代わりにそのデータを適切な場所から読み込むようにします。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
フォームからのデータは通常、`application/x-www-form-urlencoded`の「media type」を使用してエンコードされます。
|
||||
|
||||
しかし、フォームがファイルを含む場合は、`multipart/form-data`としてエンコードされます。ファイルの扱いについては次の章で説明します。
|
||||
|
||||
これらのエンコーディングやフォームフィールドの詳細については、<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr>の<code>POST</code></a>のウェブドキュメントを参照してください。
|
||||
|
||||
///
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
*path operation*で複数の`Form`パラメータを宣言することができますが、JSONとして受け取ることを期待している`Body`フィールドを宣言することはできません。なぜなら、リクエストは`application/json`の代わりに`application/x-www-form-urlencoded`を使ってボディをエンコードするからです。
|
||||
|
||||
これは **FastAPI**の制限ではなく、HTTPプロトコルの一部です。
|
||||
|
||||
///
|
||||
|
||||
## まとめ
|
||||
|
||||
フォームデータの入力パラメータを宣言するには、`Form`を使用する。
|
||||
@@ -0,0 +1,212 @@
|
||||
# レスポンスモデル
|
||||
|
||||
*path operations* のいずれにおいても、`response_model`パラメータを使用して、レスポンスのモデルを宣言することができます:
|
||||
|
||||
* `@app.get()`
|
||||
* `@app.post()`
|
||||
* `@app.put()`
|
||||
* `@app.delete()`
|
||||
* など。
|
||||
|
||||
{* ../../docs_src/response_model/tutorial001.py hl[17] *}
|
||||
|
||||
/// note | 備考
|
||||
|
||||
`response_model`は「デコレータ」メソッド(`get`、`post`など)のパラメータであることに注意してください。すべてのパラメータやボディのように、*path operation関数* のパラメータではありません。
|
||||
|
||||
///
|
||||
|
||||
Pydanticモデルの属性に対して宣言するのと同じ型を受け取るので、Pydanticモデルになることもできますが、例えば、`List[Item]`のようなPydanticモデルの`list`になることもできます。
|
||||
|
||||
FastAPIは`response_model`を使って以下のことをします:
|
||||
|
||||
* 出力データを型宣言に変換します。
|
||||
* データを検証します。
|
||||
* OpenAPIの *path operation* で、レスポンス用のJSON Schemaを追加します。
|
||||
* 自動ドキュメントシステムで使用されます。
|
||||
|
||||
しかし、最も重要なのは:
|
||||
|
||||
* 出力データをモデルのデータに限定します。これがどのように重要なのか以下で見ていきましょう。
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
レスポンスモデルは、関数の戻り値のアノテーションではなく、このパラメータで宣言されています。なぜなら、パス関数は実際にはそのレスポンスモデルを返すのではなく、`dict`やデータベースオブジェクト、あるいは他のモデルを返し、`response_model`を使用してフィールドの制限やシリアライズを行うからです。
|
||||
|
||||
///
|
||||
|
||||
## 同じ入力データの返却
|
||||
|
||||
ここでは`UserIn`モデルを宣言しています。それには平文のパスワードが含まれています:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial002.py hl[9,11] *}
|
||||
|
||||
そして、このモデルを使用して入力を宣言し、同じモデルを使って出力を宣言しています:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial002.py hl[17,18] *}
|
||||
|
||||
これで、ブラウザがパスワードを使ってユーザーを作成する際に、APIがレスポンスで同じパスワードを返すようになりました。
|
||||
|
||||
この場合、ユーザー自身がパスワードを送信しているので問題ないかもしれません。
|
||||
|
||||
しかし、同じモデルを別の*path operation*に使用すると、すべてのクライアントにユーザーのパスワードを送信してしまうことになります。
|
||||
|
||||
/// danger | 危険
|
||||
|
||||
ユーザーの平文のパスワードを保存したり、レスポンスで送信したりすることは絶対にしないでください。
|
||||
|
||||
///
|
||||
|
||||
## 出力モデルの追加
|
||||
|
||||
代わりに、平文のパスワードを持つ入力モデルと、パスワードを持たない出力モデルを作成することができます:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003.py hl[9,11,16] *}
|
||||
|
||||
ここでは、*path operation関数*がパスワードを含む同じ入力ユーザーを返しているにもかかわらず:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003.py hl[24] *}
|
||||
|
||||
...`response_model`を`UserOut`と宣言したことで、パスワードが含まれていません:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003.py hl[22] *}
|
||||
|
||||
そのため、**FastAPI** は出力モデルで宣言されていない全てのデータをフィルタリングしてくれます(Pydanticを使用)。
|
||||
|
||||
## ドキュメントを見る
|
||||
|
||||
自動ドキュメントを見ると、入力モデルと出力モデルがそれぞれ独自のJSON Schemaを持っていることが確認できます。
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/response-model/image01.png">
|
||||
|
||||
そして、両方のモデルは、対話型のAPIドキュメントに使用されます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/response-model/image02.png">
|
||||
|
||||
## レスポンスモデルのエンコーディングパラメータ
|
||||
|
||||
レスポンスモデルにはデフォルト値を設定することができます:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial004.py hl[11,13,14] *}
|
||||
|
||||
* `description: str = None`は`None`がデフォルト値です。
|
||||
* `tax: float = 10.5`は`10.5`がデフォルト値です。
|
||||
* `tags: List[str] = []` は空のリスト(`[]`)がデフォルト値です。
|
||||
|
||||
しかし、実際に保存されていない場合には結果からそれらを省略した方が良いかもしれません。
|
||||
|
||||
例えば、NoSQLデータベースに多くのオプション属性を持つモデルがあるが、デフォルト値でいっぱいの非常に長いJSONレスポンスを送信したくない場合です。
|
||||
|
||||
### `response_model_exclude_unset`パラメータの使用
|
||||
|
||||
*path operation デコレータ*に`response_model_exclude_unset=True`パラメータを設定することができます:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial004.py hl[24] *}
|
||||
|
||||
そして、これらのデフォルト値はレスポンスに含まれず、実際に設定された値のみが含まれます。
|
||||
|
||||
そのため、*path operation*にID`foo`が設定されたitemのリクエストを送ると、レスポンスは以下のようになります(デフォルト値を含まない):
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"price": 50.2
|
||||
}
|
||||
```
|
||||
|
||||
/// info | 情報
|
||||
|
||||
FastAPIはこれをするために、Pydanticモデルの`.dict()`を<a href="https://docs.pydantic.dev/latest/concepts/serialization/#modeldict" class="external-link" target="_blank">その`exclude_unset`パラメータ</a>で使用しています。
|
||||
|
||||
///
|
||||
|
||||
/// info | 情報
|
||||
|
||||
以下も使用することができます:
|
||||
|
||||
* `response_model_exclude_defaults=True`
|
||||
* `response_model_exclude_none=True`
|
||||
|
||||
`exclude_defaults`と`exclude_none`については、<a href="https://docs.pydantic.dev/latest/concepts/serialization/#modeldict" class="external-link" target="_blank">Pydanticのドキュメント</a>で説明されている通りです。
|
||||
|
||||
///
|
||||
|
||||
#### デフォルト値を持つフィールドの値を持つデータ
|
||||
|
||||
しかし、ID`bar`のitemのように、デフォルト値が設定されているモデルのフィールドに値が設定されている場合:
|
||||
|
||||
```Python hl_lines="3 5"
|
||||
{
|
||||
"name": "Bar",
|
||||
"description": "The bartenders",
|
||||
"price": 62,
|
||||
"tax": 20.2
|
||||
}
|
||||
```
|
||||
|
||||
それらはレスポンスに含まれます。
|
||||
|
||||
#### デフォルト値と同じ値を持つデータ
|
||||
|
||||
ID`baz`のitemのようにデフォルト値と同じ値を持つデータの場合:
|
||||
|
||||
```Python hl_lines="3 5 6"
|
||||
{
|
||||
"name": "Baz",
|
||||
"description": None,
|
||||
"price": 50.2,
|
||||
"tax": 10.5,
|
||||
"tags": []
|
||||
}
|
||||
```
|
||||
|
||||
FastAPIは十分に賢いので(実際には、Pydanticが十分に賢い)`description`や`tax`、`tags`はデフォルト値と同じ値を持っているにもかかわらず、明示的に設定されていることを理解しています。(デフォルトから取得するのではなく)
|
||||
|
||||
そのため、それらはJSONレスポンスに含まれることになります。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
デフォルト値は`None`だけでなく、なんでも良いことに注意してください。
|
||||
例えば、リスト(`[]`)や`10.5`の`float`などです。
|
||||
|
||||
///
|
||||
|
||||
### `response_model_include`と`response_model_exclude`
|
||||
|
||||
*path operationデコレータ*として`response_model_include`と`response_model_exclude`も使用することができます。
|
||||
|
||||
属性名を持つ`str`の`set`を受け取り、含める(残りを省略する)か、除外(残りを含む)します。
|
||||
|
||||
これは、Pydanticモデルが1つしかなく、出力からいくつかのデータを削除したい場合のクイックショートカットとして使用することができます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
それでも、これらのパラメータではなく、複数のクラスを使用して、上記のようなアイデアを使うことをおすすめします。
|
||||
|
||||
これは`response_model_include`や`response_mode_exclude`を使用していくつかの属性を省略しても、アプリケーションのOpenAPI(とドキュメント)で生成されたJSON Schemaが完全なモデルになるからです。
|
||||
|
||||
同様に動作する`response_model_by_alias`にも当てはまります。
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/response_model/tutorial005.py hl[31,37] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`{"name", "description"}`の構文はこれら2つの値をもつ`set`を作成します。
|
||||
|
||||
これは`set(["name", "description"])`と同等です。
|
||||
|
||||
///
|
||||
|
||||
#### `set`の代わりに`list`を使用する
|
||||
|
||||
もし`set`を使用することを忘れて、代わりに`list`や`tuple`を使用しても、FastAPIはそれを`set`に変換して正しく動作します:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial006.py hl[31,37] *}
|
||||
|
||||
## まとめ
|
||||
|
||||
*path operationデコレータの*`response_model`パラメータを使用して、レスポンスモデルを定義し、特にプライベートデータがフィルタリングされていることを保証します。
|
||||
|
||||
明示的に設定された値のみを返すには、`response_model_exclude_unset`を使用します。
|
||||
@@ -0,0 +1,101 @@
|
||||
# レスポンスステータスコード
|
||||
|
||||
レスポンスモデルを指定するのと同じ方法で、レスポンスに使用されるHTTPステータスコードを以下の*path operations*のいずれかの`status_code`パラメータで宣言することもできます。
|
||||
|
||||
* `@app.get()`
|
||||
* `@app.post()`
|
||||
* `@app.put()`
|
||||
* `@app.delete()`
|
||||
* など。
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
|
||||
|
||||
/// note | 備考
|
||||
|
||||
`status_code`は「デコレータ」メソッド(`get`、`post`など)のパラメータであることに注意してください。すべてのパラメータやボディのように、*path operation関数*のものではありません。
|
||||
|
||||
///
|
||||
|
||||
`status_code`パラメータはHTTPステータスコードを含む数値を受け取ります。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
`status_code`は代わりに、Pythonの<a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a>のように、`IntEnum`を受け取ることもできます。
|
||||
|
||||
///
|
||||
|
||||
これは:
|
||||
|
||||
* レスポンスでステータスコードを返します。
|
||||
* OpenAPIスキーマ(およびユーザーインターフェース)に以下のように文書化します:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/response-status-code/image01.png">
|
||||
|
||||
/// note | 備考
|
||||
|
||||
いくつかのレスポンスコード(次のセクションを参照)は、レスポンスにボディがないことを示しています。
|
||||
|
||||
FastAPIはこれを知っていて、レスポンスボディがないというOpenAPIドキュメントを生成します。
|
||||
|
||||
///
|
||||
|
||||
## HTTPステータスコードについて
|
||||
|
||||
/// note | 備考
|
||||
|
||||
すでにHTTPステータスコードが何であるかを知っている場合は、次のセクションにスキップしてください。
|
||||
|
||||
///
|
||||
|
||||
HTTPでは、レスポンスの一部として3桁の数字のステータスコードを送信します。
|
||||
|
||||
これらのステータスコードは、それらを認識するために関連付けられた名前を持っていますが、重要な部分は番号です。
|
||||
|
||||
つまり:
|
||||
|
||||
* `100`以上は「情報」のためのものです。。直接使うことはほとんどありません。これらのステータスコードを持つレスポンスはボディを持つことができません。
|
||||
* **`200`** 以上は「成功」のレスポンスのためのものです。これらは最も利用するであろうものです。
|
||||
* `200`はデフォルトのステータスコードで、すべてが「OK」であったことを意味します。
|
||||
* 別の例としては、`201`(Created)があります。これはデータベースに新しいレコードを作成した後によく使用されます。
|
||||
* 特殊なケースとして、`204`(No Content)があります。このレスポンスはクライアントに返すコンテンツがない場合に使用されます。そしてこのレスポンスはボディを持つことはできません。
|
||||
* **`300`** 以上は「リダイレクト」のためのものです。これらのステータスコードを持つレスポンスは`304`(Not Modified)を除き、ボディを持つことも持たないこともできます。
|
||||
* **`400`** 以上は「クライアントエラー」のレスポンスのためのものです。これらは、おそらく最も多用するであろう2番目のタイプです。
|
||||
* 例えば、`404`は「Not Found」レスポンスです。
|
||||
* クライアントからの一般的なエラーについては、`400`を使用することができます。
|
||||
* `500`以上はサーバーエラーのためのものです。これらを直接使うことはほとんどありません。アプリケーションコードやサーバーのどこかで何か問題が発生した場合、これらのステータスコードのいずれかが自動的に返されます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
それぞれのステータスコードとどのコードが何のためのコードなのかについて詳細は<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> HTTP レスポンスステータスコードについてのドキュメント</a>を参照してください。
|
||||
|
||||
///
|
||||
|
||||
## 名前を覚えるための近道
|
||||
|
||||
先ほどの例をもう一度見てみましょう:
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
|
||||
|
||||
`201`は「作成完了」のためのステータスコードです。
|
||||
|
||||
しかし、それぞれのコードの意味を暗記する必要はありません。
|
||||
|
||||
`fastapi.status`の便利な変数を利用することができます。
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
|
||||
|
||||
それらは便利です。それらは同じ番号を保持しており、その方法ではエディタの自動補完を使用してそれらを見つけることができます。
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/response-status-code/image02.png">
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
また、`from starlette import status`を使うこともできます。
|
||||
|
||||
**FastAPI** は、`開発者の利便性を考慮して、fastapi.status`と同じ`starlette.status`を提供しています。しかし、これはStarletteから直接提供されています。
|
||||
|
||||
///
|
||||
|
||||
## デフォルトの変更
|
||||
|
||||
後に、[高度なユーザーガイド](../advanced/response-change-status-code.md){.internal-link target=_blank}で、ここで宣言しているデフォルトとは異なるステータスコードを返す方法を見ていきます。
|
||||
@@ -0,0 +1,55 @@
|
||||
# スキーマの追加 - 例
|
||||
|
||||
JSON Schemaに追加する情報を定義することができます。
|
||||
|
||||
一般的なユースケースはこのドキュメントで示されているように`example`を追加することです。
|
||||
|
||||
JSON Schemaの追加情報を宣言する方法はいくつかあります。
|
||||
|
||||
## Pydanticの`schema_extra`
|
||||
|
||||
<a href="https://docs.pydantic.dev/latest/concepts/json_schema/#schema-customization" class="external-link" target="_blank">Pydanticのドキュメント: スキーマのカスタマイズ</a>で説明されているように、`Config`と`schema_extra`を使ってPydanticモデルの例を宣言することができます:
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial001.py hl[15,16,17,18,19,20,21,22,23] *}
|
||||
|
||||
その追加情報はそのまま出力され、JSON Schemaに追加されます。
|
||||
|
||||
## `Field`の追加引数
|
||||
|
||||
後述する`Field`、`Path`、`Query`、`Body`などでは、任意の引数を関数に渡すことでJSON Schemaの追加情報を宣言することもできます:
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial002.py hl[4,10,11,12,13] *}
|
||||
|
||||
/// warning | 注意
|
||||
|
||||
これらの追加引数が渡されても、文書化のためのバリデーションは追加されず、注釈だけが追加されることを覚えておいてください。
|
||||
|
||||
///
|
||||
|
||||
## `Body`の追加引数
|
||||
|
||||
追加情報を`Field`に渡すのと同じように、`Path`、`Query`、`Body`などでも同じことができます。
|
||||
|
||||
例えば、`Body`にボディリクエストの`example`を渡すことができます:
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial003.py hl[21,22,23,24,25,26] *}
|
||||
|
||||
## ドキュメントのUIの例
|
||||
|
||||
上記のいずれの方法でも、`/docs`の中では以下のようになります:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/body-fields/image01.png">
|
||||
|
||||
## 技術詳細
|
||||
|
||||
`example` と `examples`について...
|
||||
|
||||
JSON Schemaの最新バージョンでは<a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a>というフィールドを定義していますが、OpenAPIは`examples`を持たない古いバージョンのJSON Schemaをベースにしています。
|
||||
|
||||
そのため、OpenAPIでは同じ目的のために<a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-20" class="external-link" target="_blank">`example`</a>を独自に定義しており(`examples`ではなく`example`として)、それがdocs UI(Swagger UIを使用)で使用されています。
|
||||
|
||||
つまり、`example`はJSON Schemaの一部ではありませんが、OpenAPIの一部であり、それがdocs UIで使用されることになります。
|
||||
|
||||
## その他の情報
|
||||
|
||||
同じように、フロントエンドのユーザーインターフェースなどをカスタマイズするために、各モデルのJSON Schemaに追加される独自の追加情報を追加することができます。
|
||||
@@ -0,0 +1,197 @@
|
||||
# セキュリティ - 最初の一歩
|
||||
|
||||
あるドメインに、**バックエンド** APIを持っているとしましょう。
|
||||
|
||||
そして、別のドメインか同じドメインの違うパス(またはモバイルアプリケーションの中)に **フロントエンド**を持っています。
|
||||
|
||||
さらに、フロントエンドが**ユーザー名**と**パスワード**を使って、バックエンドで認証する方法を用意したいです。
|
||||
|
||||
**FastAPI**では、これを**OAuth2**を使用して構築できます。
|
||||
|
||||
ですが、ちょっとした必要な情報を探すために、長い仕様のすべてを読む必要はありません。
|
||||
|
||||
**FastAPI**が提供するツールを使って、セキュリティを制御してみましょう。
|
||||
|
||||
## どう見えるか
|
||||
|
||||
まずはこのコードを使って、どう動くか観察します。その後で、何が起こっているのか理解しましょう。
|
||||
|
||||
## `main.py`を作成
|
||||
|
||||
`main.py`に、下記の例をコピーします:
|
||||
|
||||
{* ../../docs_src/security/tutorial001.py *}
|
||||
|
||||
## 実行
|
||||
|
||||
/// info | 情報
|
||||
|
||||
まず<a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>をインストールします。
|
||||
|
||||
例えば、`pip install python-multipart`。
|
||||
|
||||
これは、**OAuth2**が `ユーザー名` や `パスワード` を送信するために、「フォームデータ」を使うからです。
|
||||
|
||||
///
|
||||
|
||||
例を実行します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## 確認
|
||||
|
||||
次のインタラクティブなドキュメントにアクセスしてください: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。
|
||||
|
||||
下記のように見えるでしょう:
|
||||
|
||||
<img src="/img/tutorial/security/image01.png">
|
||||
|
||||
/// check | Authorizeボタン!
|
||||
|
||||
すでにピカピカの新しい「Authorize」ボタンがあります。
|
||||
|
||||
そして、あなたの*path operation*には、右上にクリックできる小さな鍵アイコンがあります。
|
||||
|
||||
///
|
||||
|
||||
それをクリックすると、`ユーザー名`と`パスワード` (およびその他のオプションフィールド) を入力する小さな認証フォームが表示されます:
|
||||
|
||||
<img src="/img/tutorial/security/image02.png">
|
||||
|
||||
/// note | 備考
|
||||
|
||||
フォームに何を入力しても、まだうまくいきません。ですが、これから動くようになります。
|
||||
|
||||
///
|
||||
|
||||
もちろんエンドユーザーのためのフロントエンドではありません。しかし、すべてのAPIをインタラクティブにドキュメント化するための素晴らしい自動ツールです。
|
||||
|
||||
フロントエンドチームはこれを利用できます (また、あなたも利用できます) 。
|
||||
|
||||
サードパーティのアプリケーションやシステムでも使用可能です。
|
||||
|
||||
また、同じアプリケーションのデバッグ、チェック、テストのためにも利用できます。
|
||||
|
||||
## `パスワード` フロー
|
||||
|
||||
では、少し話を戻して、どうなっているか理解しましょう。
|
||||
|
||||
`パスワード`の「フロー」は、OAuth2で定義されているセキュリティと認証を扱う方法 (「フロー」) の1つです。
|
||||
|
||||
OAuth2は、バックエンドやAPIがユーザーを認証するサーバーから独立したものとして設計されていました。
|
||||
|
||||
しかし、この場合、同じ**FastAPI**アプリケーションがAPIと認証を処理します。
|
||||
|
||||
そこで、簡略化した箇所から見直してみましょう:
|
||||
|
||||
* ユーザーはフロントエンドで`ユーザー名`と`パスワード`を入力し、`Enter`を押します。
|
||||
* フロントエンド (ユーザーのブラウザで実行中) は、`ユーザー名`と`パスワード`をAPIの特定のURL (`tokenUrl="token"`で宣言された) に送信します。
|
||||
* APIは`ユーザー名`と`パスワード`をチェックし、「トークン」を返却します (まだ実装していません)。
|
||||
* 「トークン」はただの文字列であり、あとでこのユーザーを検証するために使用します。
|
||||
* 通常、トークンは時間が経つと期限切れになるように設定されています。
|
||||
* トークンが期限切れの場合は、再度ログインする必要があります。
|
||||
* トークンが盗まれたとしても、リスクは低いです。永久キーのように永遠に機能するものではありません(ほとんどの場合)。
|
||||
* フロントエンドはそのトークンを一時的にどこかに保存します。
|
||||
* ユーザーがフロントエンドでクリックして、フロントエンドのWebアプリの別のセクションに移動します。
|
||||
* フロントエンドはAPIからさらにデータを取得する必要があります。
|
||||
* しかし、特定のエンドポイントの認証が必要です。
|
||||
* したがって、APIで認証するため、HTTPヘッダー`Authorization`に`Bearer`の文字列とトークンを加えた値を送信します。
|
||||
* トークンに`foobar`が含まれている場合、`Authorization`ヘッダーの内容は次のようになります: `Bearer foobar`。
|
||||
|
||||
## **FastAPI**の`OAuth2PasswordBearer`
|
||||
|
||||
**FastAPI**は、これらのセキュリティ機能を実装するために、抽象度の異なる複数のツールを提供しています。
|
||||
|
||||
この例では、**Bearer**トークンを使用して**OAuth2**を**パスワード**フローで使用します。これには`OAuth2PasswordBearer`クラスを使用します。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
「bearer」トークンが、唯一の選択肢ではありません。
|
||||
|
||||
しかし、私たちのユースケースには最適です。
|
||||
|
||||
あなたがOAuth2の専門家で、あなたのニーズに適した別のオプションがある理由を正確に知っている場合を除き、ほとんどのユースケースに最適かもしれません。
|
||||
|
||||
その場合、**FastAPI**はそれを構築するためのツールも提供します。
|
||||
|
||||
///
|
||||
|
||||
`OAuth2PasswordBearer` クラスのインスタンスを作成する時に、パラメーター`tokenUrl`を渡します。このパラメーターには、クライアント (ユーザーのブラウザで動作するフロントエンド) がトークンを取得するために`ユーザー名`と`パスワード`を送信するURLを指定します。
|
||||
|
||||
{* ../../docs_src/security/tutorial001.py hl[6] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
ここで、`tokenUrl="token"`は、まだ作成していない相対URL`token`を指します。相対URLなので、`./token`と同じです。
|
||||
|
||||
相対URLを使っているので、APIが`https://example.com/`にある場合、`https://example.com/token`を参照します。しかし、APIが`https://example.com/api/v1/`にある場合は`https://example.com/api/v1/token`を参照することになります。
|
||||
|
||||
相対 URL を使うことは、[プロキシと接続](../../advanced/behind-a-proxy.md){.internal-link target=_blank}のような高度なユースケースでもアプリケーションを動作させ続けるために重要です。
|
||||
|
||||
///
|
||||
|
||||
このパラメーターはエンドポイント/ *path operation*を作成しません。しかし、URL`/token`はクライアントがトークンを取得するために使用するものであると宣言します。この情報は OpenAPI やインタラクティブな API ドキュメントシステムで使われます。
|
||||
|
||||
実際のpath operationもすぐに作ります。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
非常に厳格な「Pythonista」であれば、パラメーター名のスタイルが`token_url`ではなく`tokenUrl`であることを気に入らないかもしれません。
|
||||
|
||||
それはOpenAPI仕様と同じ名前を使用しているからです。そのため、これらのセキュリティスキームについてもっと調べる必要がある場合は、それをコピーして貼り付ければ、それについての詳細な情報を見つけることができます。
|
||||
|
||||
///
|
||||
|
||||
変数`oauth2_scheme`は`OAuth2PasswordBearer`のインスタンスですが、「呼び出し可能」です。
|
||||
|
||||
次のように、呼ぶことができます:
|
||||
|
||||
```Python
|
||||
oauth2_scheme(some, parameters)
|
||||
```
|
||||
|
||||
そのため、`Depends`と一緒に使うことができます。
|
||||
|
||||
### 使い方
|
||||
|
||||
これで`oauth2_scheme`を`Depends`で依存関係に渡すことができます。
|
||||
|
||||
{* ../../docs_src/security/tutorial001.py hl[10] *}
|
||||
|
||||
この依存関係は、*path operation function*のパラメーター`token`に代入される`str`を提供します。
|
||||
|
||||
**FastAPI**は、この依存関係を使用してOpenAPIスキーマ (および自動APIドキュメント) で「セキュリティスキーム」を定義できることを知っています。
|
||||
|
||||
/// info | 技術詳細
|
||||
|
||||
**FastAPI**は、`OAuth2PasswordBearer` クラス (依存関係で宣言されている) を使用してOpenAPIのセキュリティスキームを定義できることを知っています。これは`fastapi.security.oauth2.OAuth2`、`fastapi.security.base.SecurityBase`を継承しているからです。
|
||||
|
||||
OpenAPIと統合するセキュリティユーティリティ (および自動APIドキュメント) はすべて`SecurityBase`を継承しています。それにより、**FastAPI**はそれらをOpenAPIに統合する方法を知ることができます。
|
||||
|
||||
///
|
||||
|
||||
## どのように動作するか
|
||||
|
||||
リクエストの中に`Authorization`ヘッダーを探しに行き、その値が`Bearer`と何らかのトークンを含んでいるかどうかをチェックし、そのトークンを`str`として返します。
|
||||
|
||||
もし`Authorization`ヘッダーが見つからなかったり、値が`Bearer`トークンを持っていなかったりすると、401 ステータスコードエラー (`UNAUTHORIZED`) で直接応答します。
|
||||
|
||||
トークンが存在するかどうかをチェックしてエラーを返す必要はありません。関数が実行された場合、そのトークンに`str`が含まれているか確認できます。
|
||||
|
||||
インタラクティブなドキュメントですでに試すことができます:
|
||||
|
||||
<img src="/img/tutorial/security/image03.png">
|
||||
|
||||
まだトークンの有効性を検証しているわけではありませんが、これはもう始まっています。
|
||||
|
||||
## まとめ
|
||||
|
||||
つまり、たった3~4行の追加で、すでに何らかの基礎的なセキュリティの形になっています。
|
||||
@@ -0,0 +1,106 @@
|
||||
# 現在のユーザーの取得
|
||||
|
||||
一つ前の章では、(依存性注入システムに基づいた)セキュリティシステムは、 *path operation関数* に `str` として `token` を与えていました:
|
||||
|
||||
{* ../../docs_src/security/tutorial001.py hl[10] *}
|
||||
|
||||
しかし、それはまだそんなに有用ではありません。
|
||||
|
||||
現在のユーザーを取得するようにしてみましょう。
|
||||
|
||||
## ユーザーモデルの作成
|
||||
|
||||
まずは、Pydanticのユーザーモデルを作成しましょう。
|
||||
|
||||
ボディを宣言するのにPydanticを使用するのと同じやり方で、Pydanticを別のどんなところでも使うことができます:
|
||||
|
||||
{* ../../docs_src/security/tutorial002.py hl[5,12:16] *}
|
||||
|
||||
## 依存関係 `get_current_user` を作成
|
||||
|
||||
依存関係 `get_current_user` を作ってみましょう。
|
||||
|
||||
依存関係はサブ依存関係を持つことができるのを覚えていますか?
|
||||
|
||||
`get_current_user` は前に作成した `oauth2_scheme` と同じ依存関係を持ちます。
|
||||
|
||||
以前直接 *path operation* の中でしていたのと同じように、新しい依存関係である `get_current_user` は `str` として `token` を受け取るようになります:
|
||||
|
||||
{* ../../docs_src/security/tutorial002.py hl[25] *}
|
||||
|
||||
## ユーザーの取得
|
||||
|
||||
`get_current_user` は作成した(偽物の)ユーティリティ関数を使って、 `str` としてトークンを受け取り、先ほどのPydanticの `User` モデルを返却します:
|
||||
|
||||
{* ../../docs_src/security/tutorial002.py hl[19:22,26:27] *}
|
||||
|
||||
## 現在のユーザーの注入
|
||||
|
||||
ですので、 `get_current_user` に対して同様に *path operation* の中で `Depends` を利用できます。
|
||||
|
||||
{* ../../docs_src/security/tutorial002.py hl[31] *}
|
||||
|
||||
Pydanticモデルの `User` として、 `current_user` の型を宣言することに注意してください。
|
||||
|
||||
その関数の中ですべての入力補完や型チェックを行う際に役に立ちます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
リクエストボディはPydanticモデルでも宣言できることを覚えているかもしれません。
|
||||
|
||||
ここでは `Depends` を使っているおかげで、 **FastAPI** が混乱することはありません。
|
||||
|
||||
///
|
||||
|
||||
/// check | 確認
|
||||
|
||||
依存関係システムがこのように設計されているおかげで、 `User` モデルを返却する別の依存関係(別の"dependables")を持つことができます。
|
||||
|
||||
同じデータ型を返却する依存関係は一つだけしか持てない、という制約が入ることはないのです。
|
||||
|
||||
///
|
||||
|
||||
## 別のモデル
|
||||
|
||||
これで、*path operation関数* の中で現在のユーザーを直接取得し、`Depends` を使って、 **依存性注入** レベルでセキュリティメカニズムを処理できるようになりました。
|
||||
|
||||
そして、セキュリティ要件のためにどんなモデルやデータでも利用することができます。(この場合は、 Pydanticモデルの `User`)
|
||||
|
||||
しかし、特定のデータモデルやクラス、型に制限されることはありません。
|
||||
|
||||
モデルを、 `id` と `email` は持つが、 `username` は全く持たないようにしたいですか? わかりました。同じ手段でこうしたこともできます。
|
||||
|
||||
ある `str` だけを持ちたい? あるいはある `dict` だけですか? それとも、データベースクラスのモデルインスタンスを直接持ちたいですか? すべて同じやり方で機能します。
|
||||
|
||||
実際には、あなたのアプリケーションにはログインするようなユーザーはおらず、単にアクセストークンを持つロボットやボット、別のシステムがありますか?ここでも、全く同じようにすべて機能します。
|
||||
|
||||
あなたのアプリケーションに必要なのがどんな種類のモデル、どんな種類のクラス、どんな種類のデータベースであったとしても、 **FastAPI** は依存性注入システムでカバーしてくれます。
|
||||
|
||||
|
||||
## コードサイズ
|
||||
|
||||
この例は冗長に見えるかもしれません。セキュリティとデータモデルユーティリティ関数および *path operations* が同じファイルに混在しているということを覚えておいてください。
|
||||
|
||||
しかし、ここに重要なポイントがあります。
|
||||
|
||||
セキュリティと依存性注入に関するものは、一度だけ書きます。
|
||||
|
||||
そして、それは好きなだけ複雑にすることができます。それでも、一箇所に、一度だけ書くのです。すべての柔軟性を備えます。
|
||||
|
||||
しかし、同じセキュリティシステムを使って何千ものエンドポイント(*path operations*)を持つことができます。
|
||||
|
||||
そして、それらエンドポイントのすべて(必要な、どの部分でも)がこうした依存関係や、あなたが作成する別の依存関係を再利用する利点を享受できるのです。
|
||||
|
||||
さらに、こうした何千もの *path operations* は、たった3行で表現できるのです:
|
||||
|
||||
{* ../../docs_src/security/tutorial002.py hl[30:32] *}
|
||||
|
||||
## まとめ
|
||||
|
||||
これで、 *path operation関数* の中で直接現在のユーザーを取得できるようになりました。
|
||||
|
||||
既に半分のところまで来ています。
|
||||
|
||||
あとは、 `username` と `password` を実際にそのユーザーやクライアントに送る、 *path operation* を追加する必要があるだけです。
|
||||
|
||||
次はそれを説明します。
|
||||
@@ -0,0 +1,106 @@
|
||||
# セキュリティ入門
|
||||
|
||||
セキュリティ、認証、認可を扱うには多くの方法があります。
|
||||
|
||||
そして、通常、それは複雑で「難しい」トピックです。
|
||||
|
||||
多くのフレームワークやシステムでは、セキュリティと認証を処理するだけで、膨大な労力とコードが必要になります(多くの場合、書かれた全コードの50%以上を占めることがあります)。
|
||||
|
||||
**FastAPI** は、セキュリティの仕様をすべて勉強して学ぶことなく、標準的な方法で簡単に、迅速に**セキュリティ**を扱うためのツールをいくつか提供します。
|
||||
|
||||
しかし、その前に、いくつかの小さな概念を確認しましょう。
|
||||
|
||||
## お急ぎですか?
|
||||
|
||||
もし、これらの用語に興味がなく、ユーザー名とパスワードに基づく認証でセキュリティを**今すぐ**確保する必要がある場合は、次の章に進んでください。
|
||||
|
||||
## OAuth2
|
||||
|
||||
OAuth2は、認証と認可を処理するためのいくつかの方法を定義した仕様です。
|
||||
|
||||
かなり広範囲な仕様で、いくつかの複雑なユースケースをカバーしています。
|
||||
|
||||
これには「サードパーティ」を使用して認証する方法が含まれています。
|
||||
|
||||
これが、「Facebook、Google、X (Twitter)、GitHubを使ってログイン」を使用したすべてのシステムの背後で使われている仕組みです。
|
||||
|
||||
### OAuth 1
|
||||
|
||||
OAuth 1というものもありましたが、これはOAuth2とは全く異なり、通信をどのように暗号化するかという仕様が直接的に含まれており、より複雑なものとなっています。
|
||||
|
||||
現在ではあまり普及していませんし、使われてもいません。
|
||||
|
||||
OAuth2は、通信を暗号化する方法を指定せず、アプリケーションがHTTPSで提供されることを想定しています。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
**デプロイ**のセクションでは、TraefikとLet's Encryptを使用して、無料でHTTPSを設定する方法が紹介されています。
|
||||
|
||||
///
|
||||
|
||||
## OpenID Connect
|
||||
|
||||
OpenID Connectは、**OAuth2**をベースにした別の仕様です。
|
||||
|
||||
これはOAuth2を拡張したもので、OAuth2ではやや曖昧だった部分を明確にし、より相互運用性を高めようとしたものです。
|
||||
|
||||
例として、GoogleのログインはOpenID Connectを使用しています(これはOAuth2がベースになっています)。
|
||||
|
||||
しかし、FacebookのログインはOpenID Connectをサポートしていません。OAuth2を独自にアレンジしています。
|
||||
|
||||
### OpenID (「OpenID Connect」ではない)
|
||||
|
||||
また、「OpenID」という仕様もありました。それは、**OpenID Connect**と同じことを解決しようとしたものですが、OAuth2に基づいているわけではありませんでした。
|
||||
|
||||
つまり、完全な追加システムだったのです。
|
||||
|
||||
現在ではあまり普及していませんし、使われてもいません。
|
||||
|
||||
## OpenAPI
|
||||
|
||||
OpenAPI(以前はSwaggerとして知られていました)は、APIを構築するためのオープンな仕様です(現在はLinux Foundationの一部になっています)。
|
||||
|
||||
**FastAPI**は、**OpenAPI**をベースにしています。
|
||||
|
||||
それが、複数の自動対話型ドキュメント・インターフェースやコード生成などを可能にしているのです。
|
||||
|
||||
OpenAPIには、複数のセキュリティ「スキーム」を定義する方法があります。
|
||||
|
||||
それらを使用することで、これらの対話型ドキュメントシステムを含む、標準ベースのツールをすべて活用できます。
|
||||
|
||||
OpenAPIでは、以下のセキュリティスキームを定義しています:
|
||||
|
||||
* `apiKey`: アプリケーション固有のキーで、これらのものから取得できます。
|
||||
* クエリパラメータ
|
||||
* ヘッダー
|
||||
* クッキー
|
||||
* `http`: 標準的なHTTP認証システムで、これらのものを含みます。
|
||||
* `bearer`: ヘッダ `Authorization` の値が `Bearer ` で、トークンが含まれます。これはOAuth2から継承しています。
|
||||
* HTTP Basic認証
|
||||
* HTTP ダイジェスト認証など
|
||||
* `oauth2`: OAuth2のセキュリティ処理方法(「フロー」と呼ばれます)のすべて。
|
||||
* これらのフローのいくつかは、OAuth 2.0認証プロバイダ(Google、Facebook、X (Twitter)、GitHubなど)を構築するのに適しています。
|
||||
* `implicit`
|
||||
* `clientCredentials`
|
||||
* `authorizationCode`
|
||||
* しかし、同じアプリケーション内で認証を直接処理するために完全に機能する特定の「フロー」があります。
|
||||
* `password`: 次のいくつかの章では、その例を紹介します。
|
||||
* `openIdConnect`: OAuth2認証データを自動的に発見する方法を定義できます。
|
||||
* この自動検出メカニズムは、OpenID Connectの仕様で定義されているものです。
|
||||
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
Google、Facebook、X (Twitter)、GitHubなど、他の認証/認可プロバイダを統合することも可能で、比較的簡単です。
|
||||
|
||||
最も複雑な問題は、それらのような認証/認可プロバイダを構築することですが、**FastAPI**は、あなたのために重い仕事をこなしながら、それを簡単に行うためのツールを提供します。
|
||||
|
||||
///
|
||||
|
||||
## **FastAPI** ユーティリティ
|
||||
|
||||
FastAPIは `fastapi.security` モジュールの中で、これらのセキュリティスキームごとにいくつかのツールを提供し、これらのセキュリティメカニズムを簡単に使用できるようにします。
|
||||
|
||||
次の章では、**FastAPI** が提供するこれらのツールを使って、あなたのAPIにセキュリティを追加する方法について見ていきます。
|
||||
|
||||
また、それがどのようにインタラクティブなドキュメントシステムに自動的に統合されるかも見ていきます。
|
||||
@@ -0,0 +1,275 @@
|
||||
# パスワード(およびハッシュ化)によるOAuth2、JWTトークンによるBearer
|
||||
|
||||
これでセキュリティの流れが全てわかったので、<abbr title="JSON Web Tokens">JWT</abbr>トークンと安全なパスワードのハッシュ化を使用して、実際にアプリケーションを安全にしてみましょう。
|
||||
|
||||
このコードは、アプリケーションで実際に使用したり、パスワードハッシュをデータベースに保存するといった用途に利用できます。
|
||||
|
||||
本章では、前章の続きから始めて、コードをアップデートしていきます。
|
||||
|
||||
## JWT について
|
||||
|
||||
JWTとは「JSON Web Tokens」の略称です。
|
||||
|
||||
JSONオブジェクトをスペースのない長く密集した文字列で表現したトークンの仕様です。例えば次のようになります:
|
||||
|
||||
```
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|
||||
```
|
||||
|
||||
これらは暗号化されていないので、誰でもコンテンツから情報を復元できてしまいます。
|
||||
|
||||
しかし、トークンは署名されているため、あなたが発行したトークンを受け取った人は、あなたが実際に発行したということを検証できます。
|
||||
|
||||
例えば、1週間の有効期限を持つトークンを作成したとします。ユーザーが翌日そのトークンを持って戻ってきたとき、そのユーザーはまだシステムにログインしていることがわかります。
|
||||
|
||||
1週間後、トークンが期限切れとなるとどうなるでしょうか?ユーザーは認可されず、新しいトークンを得るために再びサインインしなければなりません。また、ユーザー(または第三者)がトークンを修正して有効期限を変更しようとした場合、署名が一致しないため、トークンの修正を検知できます。
|
||||
|
||||
JWT トークンを使って遊んでみたいという方は、<a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a> をチェックしてください。
|
||||
|
||||
## `python-jose` のインストール
|
||||
|
||||
PythonでJWTトークンの生成と検証を行うために、`python-jose`をインストールする必要があります:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install python-jose[cryptography]
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
また、<a href="https://github.com/mpdavis/python-jose" class="external-link" target="_blank">Python-jose</a>だけではなく、暗号を扱うためのパッケージを追加で必要とします。
|
||||
|
||||
ここでは、推奨されているものを使用します:<a href="https://cryptography.io/" class="external-link" target="_blank">pyca/cryptography</a>。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
このチュートリアルでは以前、<a href="https://pyjwt.readthedocs.io/" class="external-link" target="_blank">PyJWT</a>を使用していました。
|
||||
|
||||
しかし、Python-joseは、PyJWTのすべての機能に加えて、後に他のツールと統合して構築する際におそらく必要となる可能性のあるいくつかの追加機能を提供しています。そのため、代わりにPython-joseを使用するように更新されました。
|
||||
|
||||
///
|
||||
|
||||
## パスワードのハッシュ化
|
||||
|
||||
「ハッシュ化」とは、あるコンテンツ(ここではパスワード)を、規則性のないバイト列(単なる文字列)に変換することです。
|
||||
|
||||
特徴として、全く同じ内容(全く同じパスワード)を渡すと、全く同じ規則性のないバイト列に変換されます。
|
||||
|
||||
しかし、規則性のないバイト列から元のパスワードに戻すことはできません。
|
||||
|
||||
### パスワードのハッシュ化を使う理由
|
||||
|
||||
データベースが盗まれても、ユーザーの平文のパスワードは盗まれず、ハッシュ値だけが盗まれます。
|
||||
|
||||
したがって、泥棒はそのパスワードを別のシステムで使えません(多くのユーザーはどこでも同じパスワードを使用しているため、危険性があります)。
|
||||
|
||||
## `passlib` のインストール
|
||||
|
||||
PassLib は、パスワードのハッシュを処理するための優れたPythonパッケージです。
|
||||
|
||||
このパッケージは、多くの安全なハッシュアルゴリズムとユーティリティをサポートします。
|
||||
|
||||
推奨されるアルゴリズムは「Bcrypt」です。
|
||||
|
||||
そのため、Bcryptを指定してPassLibをインストールします:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install passlib[bcrypt]
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`passlib`を使用すると、**Django**や**Flask**のセキュリティプラグインなどで作成されたパスワードを読み取れるように設定できます。
|
||||
|
||||
例えば、Djangoアプリケーションからデータベース内の同じデータをFastAPIアプリケーションと共有できるだけではなく、同じデータベースを使用してDjangoアプリケーションを徐々に移行することもできます。
|
||||
|
||||
また、ユーザーはDjangoアプリまたは**FastAPI**アプリからも、同時にログインできるようになります。
|
||||
|
||||
///
|
||||
|
||||
## パスワードのハッシュ化と検証
|
||||
|
||||
必要なツールを `passlib`からインポートします。
|
||||
|
||||
PassLib の「context」を作成します。これは、パスワードのハッシュ化と検証に使用されるものです。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
PassLibのcontextには、検証だけが許された非推奨の古いハッシュアルゴリズムを含む、様々なハッシュアルゴリズムを使用した検証機能もあります。
|
||||
|
||||
例えば、この機能を使用して、別のシステム(Djangoなど)によって生成されたパスワードを読み取って検証し、Bcryptなどの別のアルゴリズムを使用して新しいパスワードをハッシュするといったことができます。
|
||||
|
||||
そして、同時にそれらはすべてに互換性があります。
|
||||
|
||||
///
|
||||
|
||||
ユーザーから送られてきたパスワードをハッシュ化するユーティリティー関数を作成します。
|
||||
|
||||
また、受け取ったパスワードが保存されているハッシュと一致するかどうかを検証するユーティリティも作成します。
|
||||
|
||||
さらに、ユーザーを認証して返す関数も作成します。
|
||||
|
||||
{* ../../docs_src/security/tutorial004.py hl[7,48,55:56,59:60,69:75] *}
|
||||
|
||||
/// note | 備考
|
||||
|
||||
新しい(偽の)データベース`fake_users_db`を確認すると、ハッシュ化されたパスワードが次のようになっていることがわかります:`"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`
|
||||
|
||||
///
|
||||
|
||||
## JWTトークンの取り扱い
|
||||
|
||||
インストールした複数のモジュールをインポートします。
|
||||
|
||||
JWTトークンの署名に使用されるランダムな秘密鍵を生成します。
|
||||
|
||||
安全なランダム秘密鍵を生成するには、次のコマンドを使用します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ openssl rand -hex 32
|
||||
|
||||
09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
そして、出力された文字列を変数`SECRET_KEY`にコピーします。(例に記載している秘密鍵は実際に使用しないでください)
|
||||
|
||||
JWTトークンの署名に使用するアルゴリズム`"HS256"`を指定した変数`ALGORITHM`を作成します。
|
||||
|
||||
トークンの有効期限を指定した変数`ACCESS_TOKEN_EXPIRE_MINUTES`を作成します。
|
||||
|
||||
レスポンスのトークンエンドポイントで使用するPydanticモデルを定義します。
|
||||
|
||||
新しいアクセストークンを生成するユーティリティ関数を作成します。
|
||||
|
||||
{* ../../docs_src/security/tutorial004.py hl[6,12:14,28:30,78:86] *}
|
||||
|
||||
## 依存関係の更新
|
||||
|
||||
`get_current_user`を更新して、先ほどと同じトークンを受け取るようにしますが、今回はJWTトークンを使用します。
|
||||
|
||||
受け取ったトークンを復号して検証し、現在のユーザーを返します。
|
||||
|
||||
トークンが無効な場合は、すぐにHTTPエラーを返します。
|
||||
|
||||
{* ../../docs_src/security/tutorial004.py hl[89:106] *}
|
||||
|
||||
## `/token` パスオペレーションの更新
|
||||
|
||||
トークンの有効期限を表す`timedelta`を作成します。
|
||||
|
||||
JWTアクセストークンを作成し、それを返します。
|
||||
|
||||
{* ../../docs_src/security/tutorial004.py hl[115:130] *}
|
||||
|
||||
### JWTの"subject" `sub` についての技術的な詳細
|
||||
|
||||
JWTの仕様では、トークンのsubjectを表すキー`sub`があるとされています。
|
||||
|
||||
使用するかどうかは任意ですが、`sub`はユーザーの識別情報を入れるように規定されているので、ここで使用します。
|
||||
|
||||
JWTは、ユーザーを識別して、そのユーザーがAPI上で直接操作を実行できるようにする以外にも、他の用途で使用されることがあります。
|
||||
|
||||
例えば、「車」や「ブログ記事」を識別することができます。
|
||||
|
||||
そして、「ドライブ」(車の場合)や「編集」(ブログの場合)など、そのエンティティに関する権限も追加できます。
|
||||
|
||||
また、JWTトークンをユーザー(またはボット)に渡すことができます。ユーザーは、JWTトークンを使用するだけで、アカウントを持っていなくても、APIが生成したJWTトークンを使ってそれらの行動(車の運転、ブログ投稿の編集)を実行できるのです。
|
||||
|
||||
これらのアイデアを使用すると、JWTをより高度なシナリオに使用できます。
|
||||
|
||||
しかしながら、それらのエンティティのいくつかが同じIDを持つ可能性があります。例えば、`foo`(ユーザー`foo`、車 `foo`、ブログ投稿`foo`)などです。
|
||||
|
||||
IDの衝突を回避するために、ユーザーのJWTトークンを作成するとき、subキーの値にプレフィックスを付けることができます(例えば、`username:`)。したがって、この例では、`sub`の値は次のようになっている可能性があります:`username:johndoe`
|
||||
|
||||
覚えておくべき重要なことは、`sub`キーはアプリケーション全体で一意の識別子を持ち、文字列である必要があるということです。
|
||||
|
||||
## 確認
|
||||
|
||||
サーバーを実行し、ドキュメントに移動します:<a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>
|
||||
|
||||
次のようなユーザーインターフェイスが表示されます:
|
||||
|
||||
<img src="/img/tutorial/security/image07.png">
|
||||
|
||||
前回と同じ方法でアプリケーションの認可を行います。
|
||||
|
||||
次の認証情報を使用します:
|
||||
|
||||
Username: `johndoe`
|
||||
Password: `secret`
|
||||
|
||||
/// check | 確認
|
||||
|
||||
コードのどこにも平文のパスワード"`secret`"はなく、ハッシュ化されたものしかないことを確認してください。
|
||||
|
||||
///
|
||||
|
||||
<img src="/img/tutorial/security/image08.png">
|
||||
|
||||
エンドポイント`/users/me/`を呼び出すと、次のようなレスポンスが得られます:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"username": "johndoe",
|
||||
"email": "johndoe@example.com",
|
||||
"full_name": "John Doe",
|
||||
"disabled": false
|
||||
}
|
||||
```
|
||||
|
||||
<img src="/img/tutorial/security/image09.png">
|
||||
|
||||
開発者ツールを開くと、送信されるデータにはトークンだけが含まれており、パスワードはユーザーを認証してアクセストークンを取得する最初のリクエストでのみ送信され、その後は送信されないことがわかります。
|
||||
|
||||
<img src="/img/tutorial/security/image10.png">
|
||||
|
||||
/// note | 備考
|
||||
|
||||
ヘッダーの`Authorization`には、`Bearer`で始まる値があります。
|
||||
|
||||
///
|
||||
|
||||
## `scopes` を使った高度なユースケース
|
||||
|
||||
OAuth2には、「スコープ」という概念があります。
|
||||
|
||||
これらを利用して、JWTトークンに特定の権限セットを追加することができます。
|
||||
|
||||
そして、このトークンをユーザーに直接、または第三者に与えて、制限付きでAPIを操作できます。
|
||||
|
||||
これらの使用方法や**FastAPI**への統合方法については、**高度なユーザーガイド**で後ほど説明します。
|
||||
|
||||
## まとめ
|
||||
|
||||
ここまでの説明で、OAuth2やJWTなどの規格を使った安全な**FastAPI**アプリケーションを設定することができます。
|
||||
|
||||
ほとんどのフレームワークにおいて、セキュリティを扱うことは非常に複雑な課題となります。
|
||||
|
||||
簡略化しすぎたパッケージの多くは、データモデルやデータベース、利用可能な機能について多くの妥協をしなければなりません。そして、あまりにも単純化されたパッケージの中には、実はセキュリティ上の欠陥があるものもあります。
|
||||
|
||||
---
|
||||
|
||||
**FastAPI**は、どのようなデータベース、データモデル、ツールに対しても妥協することはありません。
|
||||
|
||||
そのため、プロジェクトに合わせて自由に選択することができます。
|
||||
|
||||
また、**FastAPI**は外部パッケージを統合するために複雑な仕組みを必要としないため、`passlib`や`python-jose`のようなよく整備され広く使われている多くのパッケージを直接使用することができます。
|
||||
|
||||
しかし、柔軟性、堅牢性、セキュリティを損なうことなく、可能な限りプロセスを簡素化するためのツールを提供します。
|
||||
|
||||
また、OAuth2のような安全で標準的なプロトコルを比較的簡単な方法で使用できるだけではなく、実装することもできます。
|
||||
|
||||
OAuth2の「スコープ」を使って、同じ基準でより細かい権限システムを実現する方法については、**高度なユーザーガイド**で詳しく説明しています。スコープ付きのOAuth2は、Facebook、Google、GitHub、Microsoft、X (Twitter)など、多くの大手認証プロバイダが、サードパーティのアプリケーションと自社のAPIとのやり取りをユーザーに代わって認可するために使用している仕組みです。
|
||||
@@ -0,0 +1,40 @@
|
||||
# 静的ファイル
|
||||
|
||||
`StaticFiles` を使用して、ディレクトリから静的ファイルを自動的に提供できます。
|
||||
|
||||
## `StaticFiles` の使用
|
||||
|
||||
* `StaticFiles` をインポート。
|
||||
* `StaticFiles()` インスタンスを生成し、特定のパスに「マウント」。
|
||||
|
||||
{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`from starlette.staticfiles import StaticFiles` も使用できます。
|
||||
|
||||
**FastAPI**は、開発者の利便性のために、`starlette.staticfiles` と同じ `fastapi.staticfiles` を提供します。しかし、実際にはStarletteから直接渡されています。
|
||||
|
||||
///
|
||||
|
||||
### 「マウント」とは
|
||||
|
||||
「マウント」とは、特定のパスに完全な「独立した」アプリケーションを追加することを意味します。これにより、すべてのサブパスの処理がなされます。
|
||||
|
||||
これは、マウントされたアプリケーションが完全に独立しているため、`APIRouter` とは異なります。メインアプリケーションのOpenAPIとドキュメントには、マウントされたアプリケーションの内容などは含まれません。
|
||||
|
||||
これについて詳しくは、**高度なユーザーガイド** をご覧ください。
|
||||
|
||||
## 詳細
|
||||
|
||||
最初の `"/static"` は、この「サブアプリケーション」が「マウント」されるサブパスを指します。したがって、`"/static"` から始まるパスはすべてサブアプリケーションによって処理されます。
|
||||
|
||||
`directory="static"` は、静的ファイルを含むディレクトリの名前を指します。
|
||||
|
||||
`name="static"` は、**FastAPI** が内部で使用できる名前を付けます。
|
||||
|
||||
これらのパラメータはすべて「`静的`」とは異なる場合があり、独自のアプリケーションのニーズと詳細に合わせて調整します。
|
||||
|
||||
## より詳しい情報
|
||||
|
||||
詳細とオプションについては、<a href="https://www.starlette.dev/staticfiles/" class="external-link" target="_blank">Starletteの静的ファイルに関するドキュメント</a>を確認してください。
|
||||
@@ -0,0 +1,146 @@
|
||||
# テスト
|
||||
|
||||
<a href="https://www.starlette.dev/testclient/" class="external-link" target="_blank">Starlette</a> のおかげで、**FastAPI** アプリケーションのテストは簡単で楽しいものになっています。
|
||||
|
||||
<a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> がベースなので、非常に使いやすく直感的です。
|
||||
|
||||
これを使用すると、**FastAPI** と共に <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> を直接利用できます。
|
||||
|
||||
## `TestClient` を使用
|
||||
|
||||
`TestClient` をインポートします。
|
||||
|
||||
`TestClient` を作成し、**FastAPI** に渡します。
|
||||
|
||||
`test_` から始まる名前の関数を作成します (これは `pytest` の標準的なコンベンションです)。
|
||||
|
||||
`httpx` と同じ様に `TestClient` オブジェクトを使用します。
|
||||
|
||||
チェックしたい Python の標準的な式と共に、シンプルに `assert` 文を記述します。
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
テスト関数は `async def` ではなく、通常の `def` であることに注意してください。
|
||||
|
||||
また、クライアントへの呼び出しも通常の呼び出しであり、`await` を使用しません。
|
||||
|
||||
これにより、煩雑にならずに、`pytest` を直接使用できます。
|
||||
|
||||
///
|
||||
|
||||
/// note | 技術詳細
|
||||
|
||||
`from starlette.testclient import TestClient` も使用できます。
|
||||
|
||||
**FastAPI** は開発者の利便性のために `fastapi.testclient` と同じ `starlette.testclient` を提供します。しかし、実際にはStarletteから直接渡されています。
|
||||
|
||||
///
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
FastAPIアプリケーションへのリクエストの送信とは別に、テストで `async` 関数 (非同期データベース関数など) を呼び出したい場合は、高度なチュートリアルの[Async Tests](../advanced/async-tests.md){.internal-link target=_blank} を参照してください。
|
||||
|
||||
///
|
||||
|
||||
## テストの分離
|
||||
|
||||
実際のアプリケーションでは、おそらくテストを別のファイルに保存します。
|
||||
|
||||
また、**FastAPI** アプリケーションは、複数のファイル/モジュールなどで構成されている場合もあります。
|
||||
|
||||
### **FastAPI** アプリファイル
|
||||
|
||||
**FastAPI** アプリに `main.py` ファイルがあるとします:
|
||||
|
||||
{* ../../docs_src/app_testing/main.py *}
|
||||
|
||||
### テストファイル
|
||||
|
||||
次に、テストを含む `test_main.py` ファイルを作成し、`main` モジュール (`main.py`) から `app` をインポートします:
|
||||
|
||||
{* ../../docs_src/app_testing/test_main.py *}
|
||||
|
||||
## テスト: 例の拡張
|
||||
|
||||
次に、この例を拡張し、詳細を追加して、さまざまなパーツをテストする方法を確認しましょう。
|
||||
|
||||
|
||||
### 拡張版 **FastAPI** アプリファイル
|
||||
|
||||
**FastAPI** アプリに `main_b.py` ファイルがあるとします。
|
||||
|
||||
そのファイルには、エラーを返す可能性のある `GET` オペレーションがあります。
|
||||
|
||||
また、いくつかのエラーを返す可能性のある `POST` オペレーションもあります。
|
||||
|
||||
これらの *path operation* には `X-Token` ヘッダーが必要です。
|
||||
|
||||
{* ../../docs_src/app_testing/app_b_py310/main.py *}
|
||||
|
||||
### 拡張版テストファイル
|
||||
|
||||
次に、先程のものに拡張版のテストを加えた、`test_main_b.py` を作成します。
|
||||
|
||||
{* ../../docs_src/app_testing/app_b/test_main.py *}
|
||||
|
||||
リクエストに情報を渡せるクライアントが必要で、その方法がわからない場合はいつでも、`httpx` での実現方法を検索 (Google) できます。
|
||||
|
||||
テストでも同じことを行います。
|
||||
|
||||
例えば:
|
||||
|
||||
* *パス* または *クエリ* パラメータを渡すには、それをURL自体に追加します。
|
||||
* JSONボディを渡すには、Pythonオブジェクト (例: `dict`) を `json` パラメータに渡します。
|
||||
* JSONの代わりに *フォームデータ* を送信する必要がある場合は、代わりに `data` パラメータを使用してください。
|
||||
* *ヘッダー* を渡すには、`headers` パラメータに `dict` を渡します。
|
||||
* *cookies* の場合、 `cookies` パラメータに `dict` です。
|
||||
|
||||
(`httpx` または `TestClient` を使用して) バックエンドにデータを渡す方法の詳細は、<a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPXのドキュメント</a>を確認してください。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
`TestClient` は、Pydanticモデルではなく、JSONに変換できるデータを受け取ることに注意してください。
|
||||
|
||||
テストにPydanticモデルがあり、テスト中にそのデータをアプリケーションに送信したい場合は、[JSON互換エンコーダ](encoder.md){.internal-link target=_blank} で説明されている `jsonable_encoder` が利用できます。
|
||||
|
||||
///
|
||||
|
||||
## 実行
|
||||
|
||||
後は、`pytest` をインストールするだけです:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install pytest
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
ファイルを検知し、自動テストを実行し、結果のレポートを返します。
|
||||
|
||||
以下でテストを実行します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pytest
|
||||
|
||||
================ test session starts ================
|
||||
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
|
||||
rootdir: /home/user/code/superawesome-cli/app
|
||||
plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1
|
||||
collected 6 items
|
||||
|
||||
---> 100%
|
||||
|
||||
test_main.py <span style="color: green; white-space: pre;">...... [100%]</span>
|
||||
|
||||
<span style="color: green;">================= 1 passed in 0.03s =================</span>
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,831 @@
|
||||
# 仮想環境
|
||||
|
||||
Pythonプロジェクトの作業では、**仮想環境**(または類似の仕組み)を使用し、プロジェクトごとにインストールするパッケージを分離するべきでしょう。
|
||||
|
||||
/// info | 情報
|
||||
|
||||
もし、仮想環境の概要や作成方法、使用方法について既にご存知なら、このセクションをスキップすることができます。🤓
|
||||
|
||||
///
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
**仮想環境**は、**環境変数**とは異なります。
|
||||
|
||||
**環境変数**は、プログラムが使用できるシステム内の変数です。
|
||||
|
||||
**仮想環境**は、ファイルをまとめたディレクトリのことです。
|
||||
|
||||
///
|
||||
|
||||
/// info | 情報
|
||||
このページでは、**仮想環境**の使用方法と、そのはたらきについて説明します。
|
||||
|
||||
もし**すべてを管理するツール**(Pythonのインストールも含む)を導入する準備ができているなら、<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> をお試しください。
|
||||
|
||||
///
|
||||
|
||||
## プロジェクトの作成
|
||||
|
||||
まず、プロジェクト用のディレクトリを作成します。
|
||||
|
||||
私は通常 home/user ディレクトリの中に `code` というディレクトリを用意していて、プロジェクトごとに1つのディレクトリをその中に作成しています。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Go to the home directory
|
||||
$ cd
|
||||
// Create a directory for all your code projects
|
||||
$ mkdir code
|
||||
// Enter into that code directory
|
||||
$ cd code
|
||||
// Create a directory for this project
|
||||
$ mkdir awesome-project
|
||||
// Enter into that project directory
|
||||
$ cd awesome-project
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## 仮想環境の作成
|
||||
|
||||
Pythonプロジェクトでの**初めての**作業を開始する際には、**<abbr title="他の選択肢もありますが、これはシンプルなガイドラインです">プロジェクト内</abbr>**に仮想環境を作成してください。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
これを行うのは、**プロジェクトごとに1回だけ**です。作業のたびに行う必要はありません。
|
||||
|
||||
///
|
||||
|
||||
//// tab | `venv`
|
||||
|
||||
仮想環境を作成するには、Pythonに付属している `venv` モジュールを使用できます。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python -m venv .venv
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// details | このコマンドの意味
|
||||
|
||||
- `python` : `python` というプログラムを呼び出します
|
||||
- `-m` : モジュールをスクリプトとして呼び出します。どのモジュールを呼び出すのか、この次に指定します
|
||||
- `venv` : 通常Pythonに付随してインストールされる `venv`モジュールを使用します
|
||||
- `.venv` : 仮想環境を`.venv`という新しいディレクトリに作成します
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | `uv`
|
||||
|
||||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> をインストール済みなら、仮想環境を作成するために `uv` を使うこともできます。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uv venv
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
デフォルトでは、 `uv` は `.venv` というディレクトリに仮想環境を作成します。
|
||||
|
||||
ただし、追加の引数にディレクトリ名を与えてカスタマイズすることもできます。
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
このコマンドは `.venv` というディレクトリに新しい仮想環境を作成します。
|
||||
|
||||
/// details | `.venv` またはその他の名前
|
||||
|
||||
仮想環境を別のディレクトリに作成することも可能ですが、 `.venv` と名付けるのが一般的な慣習です。
|
||||
|
||||
///
|
||||
|
||||
## 仮想環境の有効化
|
||||
|
||||
実行されるPythonコマンドやインストールされるパッケージが新しく作成した仮想環境を使用するよう、その仮想環境を有効化しましょう。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
そのプロジェクトの作業で**新しいターミナルセッション**を開始する際には、**毎回**有効化が必要です。
|
||||
|
||||
///
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/bin/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ .venv\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows Bash
|
||||
|
||||
もしWindowsでBashを使用している場合 (<a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>など):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/Scripts/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
**新しいパッケージ**を仮想環境にインストールするときには、再度**有効化**してください。
|
||||
|
||||
こうすることで、そのパッケージがインストールした**ターミナル(<abbr title="command line interface">CLI</abbr>)プログラム**を使用する場合に、仮想環境内のものが確実に使われ、グローバル環境にインストールされている別のもの(おそらく必要なものとは異なるバージョン)を誤って使用することを防ぎます。
|
||||
|
||||
///
|
||||
|
||||
## 仮想環境が有効であることを確認する
|
||||
|
||||
仮想環境が有効である(前のコマンドが正常に機能した)ことを確認します。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
これは**任意**ですが、すべてが期待通りに機能し、意図した仮想環境を使用していることを**確認する**良い方法です。
|
||||
|
||||
///
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ which python
|
||||
|
||||
/home/user/code/awesome-project/.venv/bin/python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
`.venv/bin/python` にある `python` バイナリが、プロジェクト(この場合は `awesome-project` )内に表示されていれば、正常に動作しています 🎉。
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
``` console
|
||||
$ Get-Command python
|
||||
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts\python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
`.venv\Scripts\python` にある `python` バイナリが、プロジェクト(この場合は `awesome-project` )内に表示されていれば、正常に動作しています 🎉。
|
||||
|
||||
////
|
||||
|
||||
## `pip` をアップグレードする
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> を使用している場合は、 `pip` の代わりに `uv` を使ってインストールを行うため、 `pip` をアップグレードする必要はありません 😎。
|
||||
|
||||
///
|
||||
|
||||
もしパッケージのインストールに `pip`(Pythonに標準で付属しています)を使用しているなら、 `pip` を最新バージョンに**アップグレード**しましょう。
|
||||
|
||||
パッケージのインストール中に発生する想定外のエラーの多くは、最初に `pip` をアップグレードしておくだけで解決されます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
通常、これは仮想環境を作成した直後に**一度だけ**実行します。
|
||||
|
||||
///
|
||||
|
||||
仮想環境が有効であることを(上で説明したコマンドで)確認し、アップグレードを実行しましょう:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python -m pip install --upgrade pip
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## `.gitignore` を追加する
|
||||
|
||||
**Git**を使用している場合(使用するべきでしょう)、 `.gitignore` ファイルを追加して、 `.venv` 内のあらゆるファイルをGitの管理対象から除外します。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> を使用して仮想環境を作成した場合、すでにこの作業は済んでいるので、この手順をスキップできます 😎。
|
||||
|
||||
///
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
これも、仮想環境を作成した直後に**一度だけ**実行します。
|
||||
|
||||
///
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ echo "*" > .venv/.gitignore
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// details | このコマンドの意味
|
||||
|
||||
- `echo "*"` : ターミナルに `*` というテキストを「表示」しようとします。(次の部分によってその動作が少し変わります)
|
||||
- `>` : `>` の左側のコマンドがターミナルに表示しようとする内容を、ターミナルには表示せず、 `>` の右側のファイルに書き込みます。
|
||||
- `.gitignore` : `*` を書き込むファイル名。
|
||||
|
||||
ここで、Gitにおける `*` は「すべて」を意味するので、このコマンドによって `.venv` ディレクトリ内のすべてがGitに無視されるようになります。
|
||||
|
||||
このコマンドは以下のテキストを持つ `.gitignore` ファイルを作成します:
|
||||
|
||||
```gitignore
|
||||
*
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
## パッケージのインストール
|
||||
|
||||
仮想環境を有効化した後、その中でパッケージをインストールできます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
プロジェクトに必要なパッケージをインストールまたはアップグレードする場合、これを**一度**実行します。
|
||||
|
||||
もし新しいパッケージを追加したり、バージョンをアップグレードする必要がある場合は、もう**一度この手順を繰り返し**ます。
|
||||
|
||||
///
|
||||
|
||||
### パッケージを直接インストールする
|
||||
|
||||
急いでいて、プロジェクトのパッケージ要件を宣言するファイルを使いたくない場合、パッケージを直接インストールできます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
プログラムが必要とするパッケージとバージョンをファイル(例えば `requirements.txt` や `pyproject.toml` )に記載しておくのは、(とても)良い考えです。
|
||||
|
||||
///
|
||||
|
||||
//// tab | `pip`
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "fastapi[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | `uv`
|
||||
|
||||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> を使用できるなら:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uv pip install "fastapi[standard]"
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
### `requirements.txt` からインストールする
|
||||
|
||||
もし `requirements.txt` があるなら、パッケージのインストールに使用できます。
|
||||
|
||||
//// tab | `pip`
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install -r requirements.txt
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | `uv`
|
||||
|
||||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> を使用できるなら:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uv pip install -r requirements.txt
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
/// details | `requirements.txt`
|
||||
|
||||
パッケージが記載された `requirements.txt` は以下のようになっています:
|
||||
|
||||
```requirements.txt
|
||||
fastapi[standard]==0.113.0
|
||||
pydantic==2.8.0
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
## プログラムを実行する
|
||||
|
||||
仮想環境を有効化した後、プログラムを実行できます。この際、仮想環境内のPythonと、そこにインストールしたパッケージが使用されます。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python main.py
|
||||
|
||||
Hello World
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## エディタの設定
|
||||
|
||||
プロジェクトではおそらくエディタを使用するでしょう。コード補完やインラインエラーの表示ができるように、作成した仮想環境をエディタでも使えるよう設定してください。(多くの場合、自動検出されます)
|
||||
|
||||
設定例:
|
||||
|
||||
* <a href="https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment" class="external-link" target="_blank">VS Code</a>
|
||||
* <a href="https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html" class="external-link" target="_blank">PyCharm</a>
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
この設定は通常、仮想環境を作成した際に**一度だけ**行います。
|
||||
|
||||
///
|
||||
|
||||
## 仮想環境の無効化
|
||||
|
||||
プロジェクトの作業が終了したら、その仮想環境を**無効化**できます。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ deactivate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
これにより、 `python` コマンドを実行しても、そのプロジェクト用(のパッケージがインストールされた)仮想環境から `python` プログラムを呼び出そうとはしなくなります。
|
||||
|
||||
## 作業準備完了
|
||||
|
||||
ここまでで、プロジェクトの作業を始める準備が整いました。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
上記の内容を理解したいですか?
|
||||
|
||||
もしそうなら、以下を読み進めてください。👇🤓
|
||||
|
||||
///
|
||||
|
||||
## なぜ仮想環境?
|
||||
|
||||
FastAPIを使った作業をするには、 [Python](https://www.python.org/) のインストールが必要です。
|
||||
|
||||
それから、FastAPIや、使用したいその他の**パッケージ**を**インストール**する必要があります。
|
||||
|
||||
パッケージをインストールするには、通常、Python に付属する `pip` コマンド (または同様の代替コマンド) を使用します。
|
||||
|
||||
ただし、`pip` を直接使用すると、パッケージは**グローバルなPython環境**(OS全体にインストールされたPython環境)にインストールされます。
|
||||
|
||||
### 問題点
|
||||
|
||||
では、グローバルPython環境にパッケージをインストールすることの問題点は何でしょうか?
|
||||
|
||||
ある時点で、あなたは**異なるパッケージ**に依存する多くのプログラムを書くことになるでしょう。そして、これらの中には同じパッケージの**異なるバージョン**に依存するものも出てくるでしょう。😱
|
||||
|
||||
例えば、 `philosophers-stone` (賢者の石)というプロジェクトを作成するとします。このプログラムは **`harry` (ハリー)というパッケージのバージョン `1`**に依存しています。そのため、 `harry` (ハリー)をインストールする必要があります。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
stone(philosophers-stone) -->|requires| harry-1[harry v1]
|
||||
```
|
||||
|
||||
それから、 `prisoner-of-azkaban` (アズカバンの囚人)という別のプロジェクトを作成したとします。このプロジェクトも `harry` (ハリー)に依存していますが、**`harry` (ハリー)のバージョン `3`**が必要です。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
|
||||
```
|
||||
|
||||
しかし、ここで問題になるのは、もしローカルの**仮想環境**ではなくグローバル(環境)にパッケージをインストールするなら、 `harry` (ハリー)のどのバージョンをインストールするか選ばないといけないことです。
|
||||
|
||||
例えば、 `philosophers-stone` (賢者の石)を実行するには、まず `harry` (ハリー)のバージョン `1` をインストールする必要があります:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "harry==1"
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
これにより、`harry` (ハリー)バージョン1がグローバルなPython環境にインストールされます。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph global[global env]
|
||||
harry-1[harry v1]
|
||||
end
|
||||
subgraph stone-project[philosophers-stone project]
|
||||
stone(philosophers-stone) -->|requires| harry-1
|
||||
end
|
||||
```
|
||||
|
||||
しかし、 `prisoner-of-azkaban` (アズカバンの囚人)を実行したい場合は、`harry` (ハリー)のバージョン `1` をアンインストールし、`harry` (ハリー)のバージョン `3` をインストールし直す必要があります。(あるいは、単に`harry` (ハリー)のバージョン `3` をインストールすることで、自動的にバージョン `1` がアンインストールされます)
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "harry==3"
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
このようにして、グローバル環境への `harry` (ハリー)のバージョン `3` のインストールが完了します。
|
||||
|
||||
それから、 `philosophers-stone` (賢者の石)を再び実行しようとすると、このプログラムは `harry` (ハリー)のバージョン `1` が必要なため、**動作しなくなる**可能性があります。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph global[global env]
|
||||
harry-1[<strike>harry v1</strike>]
|
||||
style harry-1 fill:#ccc,stroke-dasharray: 5 5
|
||||
harry-3[harry v3]
|
||||
end
|
||||
subgraph stone-project[philosophers-stone project]
|
||||
stone(philosophers-stone) -.-x|⛔️| harry-1
|
||||
end
|
||||
subgraph azkaban-project[prisoner-of-azkaban project]
|
||||
azkaban(prisoner-of-azkaban) --> |requires| harry-3
|
||||
end
|
||||
```
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
Pythonのパッケージでは、**新しいバージョン**で**互換性を損なう変更を避ける**よう努めるのが一般的ですが、それでも注意が必要です。すべてが正常に動作することをテストで確認してから、意図的に指定して新しいバージョンをインストールするのが良いでしょう。
|
||||
|
||||
///
|
||||
|
||||
あなたのすべての**プロジェクトが依存している**、**多数の**他の**パッケージ**が上記の問題を抱えていると想像してください。これは管理が非常に困難です。そして、**互換性のないバージョン**のパッケージを使ってプロジェクトを実行し、なぜ動作しないのか分からなくなるでしょう。
|
||||
|
||||
また、使用しているOS(Linux、Windows、macOS など)によっては、Pythonがすでにインストールされていることがあります。この場合、特定のバージョンのパッケージが**OSの動作に必要である**ことがあります。グローバル環境にパッケージをインストールすると、OSに付属するプログラムを**壊してしまう**可能性があります。
|
||||
|
||||
## パッケージのインストール先
|
||||
|
||||
Pythonをインストールしたとき、ファイルを含んだいくつかのディレクトリが作成されます。
|
||||
|
||||
これらの中には、インストールされたパッケージを保存するためのものもあります。
|
||||
|
||||
以下のコマンドを実行したとき:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Don't run this now, it's just an example 🤓
|
||||
$ pip install "fastapi[standard]"
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
FastAPIのコードを含む圧縮ファイルが、通常は [PyPI](https://pypi.org/project/fastapi/) からダウンロードされます。
|
||||
|
||||
また、FastAPIが依存する他のパッケージも**ダウンロード**されます。
|
||||
|
||||
それから、これらのファイルは**解凍**され、コンピュータのあるディレクトリに配置されます。
|
||||
|
||||
デフォルトでは、これらのファイルはPythonのインストール時に作成されるディレクトリ、つまり**グローバル環境**に配置されます。
|
||||
|
||||
## 仮想環境とは
|
||||
|
||||
すべてのパッケージをグローバル環境に配置することによって生じる問題の解決策は、作業する**プロジェクトごとの仮想環境**を使用することです。
|
||||
|
||||
仮想環境は**ディレクトリ**であり、グローバル環境と非常に似ていて、一つのプロジェクトで使う特定のパッケージ群をインストールできる場所です。
|
||||
|
||||
このようにして、それぞれのプロジェクトが独自の仮想環境(`.venv` ディレクトリ)に独自のパッケージ群を持つことができます。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph stone-project[philosophers-stone project]
|
||||
stone(philosophers-stone) --->|requires| harry-1
|
||||
subgraph venv1[.venv]
|
||||
harry-1[harry v1]
|
||||
end
|
||||
end
|
||||
subgraph azkaban-project[prisoner-of-azkaban project]
|
||||
azkaban(prisoner-of-azkaban) --->|requires| harry-3
|
||||
subgraph venv2[.venv]
|
||||
harry-3[harry v3]
|
||||
end
|
||||
end
|
||||
stone-project ~~~ azkaban-project
|
||||
```
|
||||
|
||||
## 仮想環境の有効化とは
|
||||
|
||||
仮想環境を有効にしたとき、例えば次のコマンドを実行した場合を考えます:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/bin/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ .venv\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows Bash
|
||||
|
||||
あるいは、WindowsでBashを使用している場合 (<a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>など):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/Scripts/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
これによって、いくつかの [環境変数](environment-variables.md){.internal-link target=_blank} が作成・修正され、次に実行されるコマンドで使用できるようになります。
|
||||
|
||||
これらの環境変数のひとつに、 `PATH` 変数があります。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
`PATH` 変数についての詳細は [環境変数](environment-variables.md#path環境変数){.internal-link target=_blank} を参照してください。
|
||||
|
||||
///
|
||||
|
||||
仮想環境を有効にすると、その仮想環境のパス `.venv/bin` (LinuxとmacOS)、あるいは `.venv\Scripts` (Windows)が `PATH` 変数に追加されます。
|
||||
|
||||
その環境を有効にする前の `PATH` 変数が次のようになっているとします。
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
```plaintext
|
||||
/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
これは、OSが以下のディレクトリ中でプログラムを探すことを意味します:
|
||||
|
||||
* `/usr/bin`
|
||||
* `/bin`
|
||||
* `/usr/sbin`
|
||||
* `/sbin`
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
```plaintext
|
||||
C:\Windows\System32
|
||||
```
|
||||
|
||||
これは、OSが以下のディレクトリ中でプログラムを探すことを意味します:
|
||||
|
||||
* `C:\Windows\System32`
|
||||
|
||||
////
|
||||
|
||||
仮想環境を有効にすると、 `PATH` 変数は次のようになります。
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
これは、OSが他のディレクトリを探すより前に、最初に以下のディレクトリ中でプログラムを探し始めることを意味します:
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin
|
||||
```
|
||||
|
||||
そのため、ターミナルで `python` と入力した際に、OSはPythonプログラムを以下のパスで発見し、使用します。
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin/python
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
|
||||
```
|
||||
|
||||
これは、OSが他のディレクトリを探すより前に、最初に以下のディレクトリ中でプログラムを探し始めることを意味します:
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts
|
||||
```
|
||||
|
||||
そのため、ターミナルで `python` と入力した際に、OSはPythonプログラムを以下のパスで発見し、使用します。
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts\python
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
重要な点は、仮想環境のパスを `PATH` 変数の**先頭**に配置することです。OSは利用可能な他のPythonを見つけるより**前に**、この仮想環境のPythonを見つけるようになります。このようにして、 `python` を実行したときに、他の `python` (例えばグローバル環境の `python` )ではなく、**その仮想環境の**Pythonを使用するようになります。
|
||||
|
||||
仮想環境を有効にして変更されることは他にもありますが、これが最も重要な変更のひとつです。
|
||||
|
||||
## 仮想環境の確認
|
||||
|
||||
仮想環境が有効かどうか、例えば次のように確認できます。:
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ which python
|
||||
|
||||
/home/user/code/awesome-project/.venv/bin/python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ Get-Command python
|
||||
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts\python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
これは、使用される `python` プログラムが**その仮想環境の**ものであることを意味します。
|
||||
|
||||
LinuxやmacOSでは `which` を、Windows PowerShellでは `Get-Command` を使用します。
|
||||
|
||||
このコマンドの動作は、 `PATH`変数に設定された**それぞれのパスを順に**確認していき、呼ばれている `python` プログラムを探します。そして、見つかり次第そのプログラムへの**パスを表示します**。
|
||||
|
||||
最も重要なことは、 `python` が呼ばれたときに、まさにこのコマンドで確認した "`python`" が実行されることです。
|
||||
|
||||
こうして、自分が想定通りの仮想環境にいるかを確認できます。
|
||||
|
||||
/// tip | 豆知識
|
||||
|
||||
ある仮想環境を有効にし、そのPythonを使用したまま**他のプロジェクトに移動して**しまうことは簡単に起こり得ます。
|
||||
|
||||
そして、その第二のプロジェクトは動作しないでしょう。なぜなら別のプロジェクトの仮想環境の**誤ったPython**を使用しているからです。
|
||||
|
||||
そのため、どの `python` が使用されているのか確認できることは役立ちます。🤓
|
||||
|
||||
///
|
||||
|
||||
## なぜ仮想環境を無効化するのか
|
||||
|
||||
例えば、`philosophers-stone` (賢者の石)というプロジェクトで作業をしていて、**その仮想環境を有効にし**、必要なパッケージをインストールしてその環境内で作業を進めているとします。
|
||||
|
||||
それから、**別のプロジェクト**、 `prisoner-of-azkaban` (アズカバンの囚人)に取り掛かろうとします。
|
||||
|
||||
そのプロジェクトディレクトリへ移動します:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ cd ~/code/prisoner-of-azkaban
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
もし `philosophers-stone` (賢者の石)の仮想環境を無効化していないと、`python` を実行したとき、 ターミナルは `philosophers-stone` (賢者の石)のPythonを使用しようとします。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ cd ~/code/prisoner-of-azkaban
|
||||
|
||||
$ python main.py
|
||||
|
||||
// Error importing sirius, it's not installed 😱
|
||||
Traceback (most recent call last):
|
||||
File "main.py", line 1, in <module>
|
||||
import sirius
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
しかし、その仮想環境を無効化し、 `prisoner-of-azkaban` (アズカバンの囚人)のための新しい仮想環境を有効にすれば、 `python` を実行したときに `prisoner-of-azkaban` (アズカバンの囚人)の仮想環境の Python が使用されるようになります。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ cd ~/code/prisoner-of-azkaban
|
||||
|
||||
// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎
|
||||
$ deactivate
|
||||
|
||||
// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀
|
||||
$ source .venv/bin/activate
|
||||
|
||||
// Now when you run python, it will find the package sirius installed in this virtual environment ✨
|
||||
$ python main.py
|
||||
|
||||
I solemnly swear 🐺
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## 代替手段
|
||||
|
||||
これは、あらゆる仕組みを**根本から**学ぶためのシンプルな入門ガイドです。
|
||||
|
||||
仮想環境、パッケージの依存関係(requirements)、プロジェクトの管理には、多くの**代替手段**があります。
|
||||
|
||||
準備が整い、パッケージの依存関係、仮想環境など**プロジェクト全体の管理**ツールを使いたいと考えたら、<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> を試してみることをおすすめします。
|
||||
|
||||
`uv` では以下のような多くのことができます:
|
||||
|
||||
* 異なるバージョンも含めた**Python のインストール**
|
||||
* プロジェクトごとの**仮想環境**の管理
|
||||
* **パッケージ**のインストール
|
||||
* プロジェクトのパッケージの**依存関係やバージョン**の管理
|
||||
* パッケージとそのバージョンの、依存関係を含めた**厳密な**組み合わせを保持し、これによって、本番環境で、開発環境と全く同じようにプロジェクトを実行できる(これは**locking**と呼ばれます)
|
||||
* その他のさまざまな機能
|
||||
|
||||
## まとめ
|
||||
|
||||
ここまで読みすべて理解したなら、世間の多くの開発者と比べて、仮想環境について**あなたはより多くのことを知っています**。🤓
|
||||
|
||||
これらの詳細を知ることは、将来、複雑に見える何かのデバッグにきっと役立つでしょう。しかし、その頃には、あなたは**そのすべての動作を根本から**理解しているでしょう。😎
|
||||
@@ -0,0 +1 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
Reference in New Issue
Block a user