カスタムレスポンス - HTML、ストリーム、ファイルなど¶
デフォルトでは、FastAPIはJSONResponse
を使用してレスポンスを返します。
レスポンスを直接返すで見たように、Response
を直接返すことでオーバーライドできます。
ただし、Response
(またはJSONResponse
のようなサブクラス)を直接返す場合、データは自動的に変換されず(response_model
を宣言しても)、ドキュメントも自動的に生成されません(たとえば、生成されたOpenAPIの一部として、HTTPヘッダーContent-Type
に特定の「メディアタイプ」を含めるなど)。
ただし、response_class
パラメーターを使用して、*パスオペレーションデコレータ*で、使用したいResponse
(例:任意のResponse
サブクラス)を宣言することもできます。
パスオペレーション関数から返される内容は、そのResponse
の中に入れられます。
また、JSONResponse
やUJSONResponse
のように、Response
に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とインタラクティブドキュメントで、text/html
の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ヘッダーを含め、テキストタイプの場合は文字セットを追加します。
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
ブロックに配置でき、それによって、終了後にファイルのようなオブジェクトが閉じられるようにします。
ヒント
ここでは、async
とawait
をサポートしない標準のopen()
を使用しているため、パスオペレーションは通常の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の追加レスポンス。