カスタムレスポンス - HTML、ストリーム、ファイル、その他¶
デフォルトでは、FastAPIはJSONResponse
を使用してレスポンスを返します。
直接レスポンスを返すで示したように、Response
を直接返すことでこれをオーバーライドできます。
しかし、Response
を直接返す場合(またはJSONResponse
のようなサブクラスも)、データは自動的に変換されず(response_model
を宣言しても)、ドキュメントも自動的に生成されません(例えば、生成されたOpenAPIの一部として、HTTPヘッダーContent-Type
に特定の「メディアタイプ」が含まれません)。
しかし、response_class
パラメータを使用して、パス操作デコレータで、使用したいResponse
(例:任意のResponse
サブクラス)を宣言することもできます。
パス操作関数から返されるコンテンツは、そのResponse
の中に配置されます。
そして、そのResponse
がJSONResponse
やUJSONResponse
の場合のようにJSONメディアタイプ(application/json
)を持つ場合、返されるデータは、パス操作デコレータで宣言したPydanticのresponse_model
によって自動的に変換(およびフィルタリング)されます。
注意
メディアタイプのないレスポンスクラスを使用すると、FastAPIはレスポンスにコンテンツがないことを期待するため、生成されたOpenAPIドキュメントにレスポンス形式を記述しません。
ORJSONResponse
を使用する¶
例えば、パフォーマンスを追求している場合は、orjson
をインストールして使用し、レスポンスをORJSONResponse
に設定できます。
使用したいResponse
クラス(サブクラス)をインポートし、パス操作デコレータで宣言します。
大きなレスポンスの場合、辞書を返すよりもResponse
を直接返す方がはるかに高速です。
これは、デフォルトではFastAPIが各項目を検査し、チュートリアルで説明されているJSON互換エンコーダと同じものを使用して、JSONとしてシリアル化可能であることを確認するためです。これにより、データベースモデルなどの任意のオブジェクトを返すことができます。
しかし、返されるコンテンツがJSONでシリアル化可能であると確信している場合は、コンテンツをレスポンスクラスに直接渡すことで、FastAPIがjsonable_encoder
を介してコンテンツをレスポンスクラスに渡す前の追加のオーバーヘッドを回避できます。
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI()
@app.get("/items/", response_class=ORJSONResponse)
async def read_items():
return ORJSONResponse([{"item_id": "Foo"}])
情報
パラメータresponse_class
は、レスポンスの「メディアタイプ」を定義するためにも使用されます。
この場合、HTTPヘッダーContent-Type
はapplication/json
に設定されます。
そして、OpenAPIではそのようにドキュメント化されます。
ヒント
ORJSONResponse
はFastAPIでのみ利用可能で、Starletteでは利用できません。
HTMLレスポンス¶
FastAPIから直接HTMLレスポンスを返すには、HTMLResponse
を使用します。
HTMLResponse
をインポートします。- パス操作デコレータのパラメータ
response_class
としてHTMLResponse
を渡します。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return """
<html>
<head>
<title>Some HTML in here</title>
</head>
<body>
<h1>Look ma! HTML!</h1>
</body>
</html>
"""
情報
パラメータresponse_class
は、レスポンスの「メディアタイプ」を定義するためにも使用されます。
この場合、HTTPヘッダーContent-Type
はtext/html
に設定されます。
そして、OpenAPIではそのようにドキュメント化されます。
Response
を返す¶
直接レスポンスを返すで示したように、パス操作でレスポンスを直接オーバーライドすることもできます。
上記の例と同じで、HTMLResponse
を返す場合は次のようになります。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/")
async def read_items():
html_content = """
<html>
<head>
<title>Some HTML in here</title>
</head>
<body>
<h1>Look ma! HTML!</h1>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
警告
パス操作関数によって直接返されたResponse
は、OpenAPIにドキュメント化されず(例えば、Content-Type
はドキュメント化されません)、自動対話型ドキュメントにも表示されません。
情報
もちろん、実際のContent-Type
ヘッダー、ステータスコードなどは、返されたResponse
オブジェクトから取得されます。
OpenAPIでドキュメント化し、Response
を上書きする¶
関数内からレスポンスをオーバーライドしたいが、同時にOpenAPIで「メディアタイプ」をドキュメント化したい場合は、response_class
パラメータを使用し、さらにResponse
オブジェクトを返すことができます。
response_class
はOpenAPIのパス操作をドキュメント化するためにのみ使用され、Response
はそのまま使用されます。
HTMLResponse
を直接返す¶
例えば、次のようになります。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
def generate_html_response():
html_content = """
<html>
<head>
<title>Some HTML in here</title>
</head>
<body>
<h1>Look ma! HTML!</h1>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return generate_html_response()
この例では、generate_html_response()
関数はHTMLをstr
で返す代わりに、すでにResponse
を生成して返しています。
generate_html_response()
の呼び出し結果を返すことで、デフォルトのFastAPIの動作をオーバーライドするResponse
を既に返していることになります。
しかし、response_class
にHTMLResponse
も渡しているので、FastAPIはOpenAPIとインタラクティブなドキュメントでHTMLをtext/html
としてドキュメント化する方法を知っています。
利用可能なレスポンス¶
以下に、利用可能なレスポンスの一部を示します。
Response
を使用して他のものを返すことも、カスタムサブクラスを作成することもできることに注意してください。
技術的な詳細
from starlette.responses import HTMLResponse
を使用することもできます。
FastAPI は、開発者の方々の便宜のために、starlette.responses
と同じものを fastapi.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を追加します。
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/legacy/")
def get_legacy_data():
data = """<?xml version="1.0"?>
<shampoo>
<Header>
Apply shampoo here.
</Header>
<Body>
You'll have to use soap here.
</Body>
</shampoo>
"""
return Response(content=data, media_type="application/xml")
HTMLResponse
¶
テキストまたはバイトを受け取り、上記で読んだようにHTMLレスポンスを返します。
PlainTextResponse
¶
テキストまたはバイトを受け取り、プレーンテキストレスポンスを返します。
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.get("/", response_class=PlainTextResponse)
async def main():
return "Hello World"
JSONResponse
¶
データを受け取り、application/json
でエンコードされたレスポンスを返します。
これは、上記で読んだように、FastAPIでデフォルトで使用されるレスポンスです。
ORJSONResponse
¶
上記で読んだように、orjson
を使用した高速な代替JSONレスポンス。
情報
これは、例えばpip install orjson
でorjson
をインストールする必要があります。
UJSONResponse
¶
ujson
を使用した代替JSONレスポンス。
情報
これは、例えばpip install ujson
でujson
をインストールする必要があります。
警告
ujson
は、いくつかのエッジケースの処理において、Pythonの組み込み実装よりも注意深くありません。
from fastapi import FastAPI
from fastapi.responses import UJSONResponse
app = FastAPI()
@app.get("/items/", response_class=UJSONResponse)
async def read_items():
return [{"item_id": "Foo"}]
ヒント
ORJSONResponse
の方が高速な代替手段である可能性があります。
RedirectResponse
¶
HTTPリダイレクトを返します。デフォルトでは307ステータスコード(一時的なリダイレクト)を使用します。
RedirectResponse
を直接返すことができます。
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/typer")
async def redirect_typer():
return RedirectResponse("https://typer.dokyumento.jp")
または、response_class
パラメータで使用することもできます。
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/fastapi", response_class=RedirectResponse)
async def redirect_fastapi():
return "https://fastapi.dokyumento.jp"
そうすると、パス操作関数から直接URLを返すことができます。
この場合、使用されるstatus_code
は、RedirectResponse
のデフォルトである307
になります。
status_code
パラメータとresponse_class
パラメータを組み合わせて使用することもできます。
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
async def redirect_pydantic():
return "https://docs.pydantic.dev/"
StreamingResponse
¶
非同期ジェネレータまたは通常のジェネレータ/イテレータを受け取り、レスポンスボディをストリーミングします。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def fake_video_streamer():
for i in range(10):
yield b"some fake video bytes"
@app.get("/")
async def main():
return StreamingResponse(fake_video_streamer())
ファイルライクオブジェクトとStreamingResponse
を使用する¶
ファイルライクオブジェクト(例:open()
によって返されるオブジェクト)がある場合、そのファイルライクオブジェクトを反復処理するジェネレータ関数を作成できます。
そうすることで、最初にすべてをメモリに読み込む必要がなく、そのジェネレータ関数をStreamingResponse
に渡し、それを返すことができます。
これには、クラウドストレージ、ビデオ処理などと対話するための多くのライブラリが含まれます。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/")
def main():
def iterfile(): # (1)
with open(some_file_path, mode="rb") as file_like: # (2)
yield from file_like # (3)
return StreamingResponse(iterfile(), media_type="video/mp4")
- これはジェネレータ関数です。内部に
yield
ステートメントが含まれているため「ジェネレータ関数」です。 with
ブロックを使用することで、ジェネレータ関数が完了した後(つまり、レスポンスの送信が完了した後)、ファイルライクオブジェクトが閉じられることを保証します。-
この
yield from
は、file_like
というものを反復処理するように関数に指示します。そして、反復処理された各部分について、その部分がこのジェネレータ関数(iterfile
)から来ているかのように生成します。つまり、内部的に「生成」の作業を他のものに転送するジェネレータ関数です。
このようにすることで、
with
ブロックに入れることができ、その方法で、ファイルライクオブジェクトが終了後に閉じられることを保証します。
ヒント
ここで、標準のopen()
はasync
とawait
をサポートしないため、パス操作を通常のdef
で宣言していることに注意してください。
FileResponse
¶
ファイルを非同期でストリーミングしてレスポンスとして返します。
他のレスポンスタイプとは異なる引数セットを受け取ってインスタンス化します。
path
- ストリームするファイルへのファイルパス。headers
- 含めるカスタムヘッダー(辞書形式)。media_type
- メディアタイプを示す文字列。設定されていない場合、ファイル名またはパスからメディアタイプが推測されます。filename
- 設定されている場合、これはレスポンスのContent-Disposition
に含まれます。
ファイルレスポンスには、適切なContent-Length
、Last-Modified
、およびETag
ヘッダーが含まれます。
from fastapi import FastAPI
from fastapi.responses import FileResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/")
async def main():
return FileResponse(some_file_path)
response_class
パラメータを使用することもできます。
from fastapi import FastAPI
from fastapi.responses import FileResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/", response_class=FileResponse)
async def main():
return some_file_path
この場合、パス操作関数から直接ファイルパスを返すことができます。
カスタムレスポンスクラス¶
Response
を継承した独自のカスタムレスポンスクラスを作成して使用できます。
例えば、orjson
を使用したいが、付属のORJSONResponse
クラスでは使用されていないカスタム設定を使いたいとします。
インデントされフォーマットされたJSONを返したいので、orjsonオプションorjson.OPT_INDENT_2
を使用したいとします。
CustomORJSONResponse
を作成できます。主な作業は、コンテンツをbytes
として返すResponse.render(content)
メソッドを作成することです。
from typing import Any
import orjson
from fastapi import FastAPI, Response
app = FastAPI()
class CustomORJSONResponse(Response):
media_type = "application/json"
def render(self, content: Any) -> bytes:
assert orjson is not None, "orjson must be installed"
return orjson.dumps(content, option=orjson.OPT_INDENT_2)
@app.get("/", response_class=CustomORJSONResponse)
async def main():
return {"message": "Hello World"}
今度は、これを返す代わりに
{"message": "Hello World"}
...このレスポンスはこれを返します
{
"message": "Hello World"
}
もちろん、JSONをフォーマットするよりも、これを利用するはるかに良い方法が見つかるでしょう。😉
デフォルトのレスポンスクラス¶
FastAPIクラスインスタンスまたはAPIRouter
を作成するときに、デフォルトで使用するレスポンスクラスを指定できます。
これを定義するパラメータはdefault_response_class
です。
以下の例では、FastAPIは、すべてのパス操作で、JSONResponse
の代わりにORJSONResponse
をデフォルトで使用します。
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI(default_response_class=ORJSONResponse)
@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]
ヒント
これまでと同様に、パス操作でresponse_class
をオーバーライドすることは可能です。
追加のドキュメント¶
また、responses
を使用して、OpenAPIでメディアタイプやその他の多くの詳細を宣言することもできます。OpenAPIにおける追加のレスポンス。