20 KiB
レスポンスモデル - 戻り値の型
path operation 関数の戻り値の型にアノテーションを付けることで、レスポンスに使用される型を宣言できます。
関数パラメータの入力データと同じように 型アノテーション を使用できます。Pydanticモデル、リスト、辞書、整数や真偽値などのスカラー値を使用できます。
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPIはこの戻り値の型を使って以下を行います:
- 返却データを検証します。
- データが不正(例: フィールドが欠けている)であれば、それはあなたのアプリコードが壊れていて、返すべきものを返していないことを意味し、不正なデータを返す代わりにサーバーエラーを返します。これにより、あなたとクライアントは、期待されるデータとデータ形状を受け取れることを確実にできます。
- OpenAPIの path operation に、レスポンス用の JSON Schema を追加します。
- これは自動ドキュメントで使用されます。
- 自動クライアントコード生成ツールでも使用されます。
しかし、最も重要なのは:
- 戻り値の型で定義された内容に合わせて、出力データを制限しフィルタリングします。
- これはセキュリティの観点で特に重要です。以下で詳しく見ていきます。
response_modelパラメータ
型が宣言している内容とまったく同じではないデータを返す必要がある、またはそうしたいケースがあります。
例えば、辞書を返す、またはデータベースオブジェクトを返したいが、Pydanticモデルとして宣言したい場合があります。こうすることで、Pydanticモデルが返したオブジェクト(例: 辞書やデータベースオブジェクト)のドキュメント化、バリデーションなどをすべて行ってくれます。
戻り値の型アノテーションを追加すると、ツールやエディタが(正しく)エラーとして、関数が宣言した型(例: Pydanticモデル)とは異なる型(例: dict)を返していると警告します。
そのような場合、戻り値の型の代わりに、path operation デコレータのパラメータ response_model を使用できます。
response_modelパラメータは、いずれの path operation でも使用できます:
@app.get()@app.post()@app.put()@app.delete()- など。
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | 備考
response_modelは「デコレータ」メソッド(get、postなど)のパラメータであることに注意してください。すべてのパラメータやボディのように、path operation 関数 のパラメータではありません。
///
response_modelは、Pydanticモデルのフィールドで宣言するのと同じ型を受け取ります。そのため、Pydanticモデルにもできますし、例えば List[Item] のように、Pydanticモデルの list にもできます。
FastAPIはこの response_model を使って、データのドキュメント化や検証などを行い、さらに出力データを型宣言に合わせて変換・フィルタリングします。
/// tip | 豆知識
エディタやmypyなどで厳密な型チェックをしている場合、関数の戻り値の型を Any として宣言できます。
そうすると、意図的に何でも返していることをエディタに伝えられます。それでもFastAPIは response_model を使って、データのドキュメント化、検証、フィルタリングなどを行います。
///
response_modelの優先順位
戻り値の型と response_model の両方を宣言した場合、response_model が優先され、FastAPIで使用されます。
これにより、レスポンスモデルとは異なる型を返している場合でも、エディタやmypyなどのツールで使用するために関数へ正しい型アノテーションを追加できます。それでもFastAPIは response_model を使用してデータの検証やドキュメント化などを実行できます。
また response_model=None を使用して、そのpath operationのレスポンスモデル生成を無効化することもできます。これは、Pydanticのフィールドとして有効ではないものに対して型アノテーションを追加する場合に必要になることがあります。以下のセクションのいずれかで例を示します。
同じ入力データの返却
ここでは UserIn モデルを宣言しています。これには平文のパスワードが含まれます:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | 情報
EmailStr を使用するには、最初に email-validator をインストールしてください。
仮想環境{.internal-link target=_blank}を作成して有効化してから、例えば次のようにインストールしてください:
$ pip install email-validator
または次のようにします:
$ pip install "pydantic[email]"
///
そして、このモデルを使用して入力を宣言し、同じモデルを使って出力を宣言しています:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
これで、ブラウザがパスワードを使ってユーザーを作成する際に、APIがレスポンスで同じパスワードを返すようになりました。
この場合、同じユーザーがパスワードを送信しているので問題ないかもしれません。
しかし、同じモデルを別のpath operationに使用すると、すべてのクライアントにユーザーのパスワードを送信してしまう可能性があります。
/// danger | 警告
すべての注意点を理解していて、自分が何をしているか分かっている場合を除き、ユーザーの平文のパスワードを保存したり、このようにレスポンスで送信したりしないでください。
///
出力モデルの追加
代わりに、平文のパスワードを持つ入力モデルと、パスワードを持たない出力モデルを作成できます:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
ここでは、path operation 関数がパスワードを含む同じ入力ユーザーを返しているにもかかわらず:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...response_modelを、パスワードを含まない UserOut モデルとして宣言しました:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
そのため、FastAPI は出力モデルで宣言されていないすべてのデータをフィルタリングしてくれます(Pydanticを使用)。
response_modelまたは戻り値の型
このケースでは2つのモデルが異なるため、関数の戻り値の型を UserOut としてアノテーションすると、エディタやツールは、異なるクラスなので不正な型を返していると警告します。
そのため、この例では response_model パラメータで宣言する必要があります。
...しかし、これを解決する方法を以下で確認しましょう。
戻り値の型とデータフィルタリング
前の例から続けます。関数に1つの型をアノテーションしたい一方で、関数からは実際にはより多くのデータを含むものを返せるようにしたいとします。
FastAPIにはレスポンスモデルを使用してデータをフィルタリングし続けてほしいです。つまり、関数がより多くのデータを返しても、レスポンスにはレスポンスモデルで宣言されたフィールドのみが含まれます。
前の例ではクラスが異なるため response_model パラメータを使う必要がありました。しかしそれは、エディタやツールによる関数の戻り値の型チェックのサポートを受けられないことも意味します。
しかし、このようなことが必要になる多くのケースでは、この例のようにモデルでデータの一部をフィルタ/削除したいだけです。
そのような場合、クラスと継承を利用して関数の型アノテーションを活用し、エディタやツールのサポートを改善しつつ、FastAPIのデータフィルタリングも得られます。
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
これにより、このコードは型として正しいためエディタやmypyからのツール支援を得られますし、FastAPIによるデータフィルタリングも得られます。
これはどのように動作するのでしょうか?確認してみましょう。🤓
型アノテーションとツール支援
まず、エディタ、mypy、その他のツールがこれをどう見るかを見てみます。
BaseUser には基本フィールドがあります。次に UserIn が BaseUser を継承して password フィールドを追加するため、両方のモデルのフィールドがすべて含まれます。
関数の戻り値の型を BaseUser としてアノテーションしますが、実際には UserIn インスタンスを返しています。
エディタやmypyなどのツールはこれに文句を言いません。typingの観点では、UserIn は BaseUser のサブクラスであり、期待されるものが BaseUser であれば UserIn は有効な型だからです。
FastAPIのデータフィルタリング
一方FastAPIでは、戻り値の型を見て、返す内容にその型で宣言されたフィールドだけが含まれることを確認します。
FastAPIは、返却データのフィルタリングにクラス継承の同じルールが使われてしまわないようにするため、内部でPydanticを使っていくつかの処理を行っています。そうでないと、期待以上に多くのデータを返してしまう可能性があります。
この方法で、ツール支援付きの型アノテーションとデータフィルタリングの両方という、いいとこ取りができます。
ドキュメントを見る
自動ドキュメントを見ると、入力モデルと出力モデルがそれぞれ独自のJSON Schemaを持っていることが確認できます:
そして、両方のモデルは対話型のAPIドキュメントに使用されます:
その他の戻り値の型アノテーション
Pydanticフィールドとして有効ではないものを返し、ツール(エディタやmypyなど)が提供するサポートを得るためだけに、関数でそれをアノテーションするケースがあるかもしれません。
レスポンスを直接返す
最も一般的なケースは、高度なドキュメントで後述する「Responseを直接返す」{.internal-link target=_blank}場合です。
{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
このシンプルなケースは、戻り値の型アノテーションが Response のクラス(またはサブクラス)であるため、FastAPIが自動的に処理します。
また RedirectResponse と JSONResponse の両方は Response のサブクラスなので、ツールも型アノテーションが正しいとして問題にしません。
Responseのサブクラスをアノテーションする
型アノテーションで Response のサブクラスを使うこともできます:
{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
これは RedirectResponse が Response のサブクラスであり、FastAPIがこのシンプルなケースを自動処理するため、同様に動作します。
無効な戻り値の型アノテーション
しかし、Pydantic型として有効ではない別の任意のオブジェクト(例: データベースオブジェクト)を返し、関数でそのようにアノテーションすると、FastAPIはその型アノテーションからPydanticレスポンスモデルを作成しようとして失敗します。
同様に、unionのように、複数の型のうち1つ以上がPydantic型として有効でないものを含む場合も起こります。例えば次は失敗します 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...これは、型アノテーションがPydantic型ではなく、単一の Response クラス(またはサブクラス)でもないために失敗します。Response と dict の間のunion(どちらか)になっているからです。
レスポンスモデルを無効化する
上の例を続けると、FastAPIが実行するデフォルトのデータ検証、ドキュメント化、フィルタリングなどを行いたくないこともあるでしょう。
しかし、エディタや型チェッカー(例: mypy)などのツール支援を得るために、関数の戻り値の型アノテーションは残したいかもしれません。
その場合、response_model=None を設定することでレスポンスモデルの生成を無効にできます:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
これによりFastAPIはレスポンスモデル生成をスキップし、FastAPIアプリケーションに影響させずに必要な戻り値の型アノテーションを付けられます。🤓
レスポンスモデルのエンコーディングパラメータ
レスポンスモデルには次のようにデフォルト値を設定できます:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
description: Union[str, None] = None(またはPython 3.10ではstr | None = 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_py310.py hl[22] *}
そうすると、デフォルト値はレスポンスに含まれず、実際に設定された値のみが含まれます。
そのため、ID foo のitemに対してその path operation へリクエストを送ると、レスポンスは以下のようになります(デフォルト値を含まない):
{
"name": "Foo",
"price": 50.2
}
/// info | 情報
以下も使用できます:
response_model_exclude_defaults=Trueresponse_model_exclude_none=True
exclude_defaults と exclude_none については、Pydanticのドキュメントで説明されている通りです。
///
デフォルト値を持つフィールドに値があるデータ
しかし、ID bar のitemのように、デフォルト値が設定されているモデルのフィールドに値が設定されている場合:
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
それらはレスポンスに含まれます。
デフォルト値と同じ値を持つデータ
ID baz のitemのようにデフォルト値と同じ値を持つデータの場合:
{
"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_model_exclude を使っていくつかの属性を省略しても、アプリのOpenAPI(とドキュメント)で生成されるJSON Schemaが完全なモデルのままになるためです。
同様に動作する response_model_by_alias にも当てはまります。
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | 豆知識
{"name", "description"} の構文は、それら2つの値を持つ set を作成します。
これは set(["name", "description"]) と同等です。
///
setの代わりにlistを使用する
もし set を使用することを忘れて、代わりに list や tuple を使用しても、FastAPIはそれを set に変換して正しく動作します:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
まとめ
path operation デコレータのパラメータ response_model を使用してレスポンスモデルを定義し、とくにプライベートデータがフィルタリングされることを保証します。
明示的に設定された値のみを返すには、response_model_exclude_unset を使用します。