コンテンツにスキップ

カスタムレスポンス - HTML、ストリーム、ファイル、その他のレスポンス

デフォルトでは、FastAPIJSONResponse を使ってレスポンスを返します。

レスポンスを直接返すで見たように、 Response を直接返すことでこの挙動をオーバーライドできます。

しかし、Response を直接返すと、データは自動的に変換されず、ドキュメントも自動生成されません (例えば、生成されるOpenAPIの一部としてHTTPヘッダー Content-Type に特定の「メディアタイプ」を含めるなど) 。

しかし、path operationデコレータ に、使いたい Response を宣言することもできます。

path operation関数 から返されるコンテンツは、その Response に含まれます。

そしてもし、Response が、JSONResponseUJSONResponse の場合のようにJSONメディアタイプ (application/json) ならば、データは path operationデコレータ に宣言したPydantic response_model により自動的に変換 (もしくはフィルタ) されます。

備考

メディアタイプを指定せずにレスポンスクラスを利用すると、FastAPIは何もコンテンツがないことを期待します。そのため、生成されるOpenAPIドキュメントにレスポンスフォーマットが記載されません。

ORJSONResponse を使う

例えば、パフォーマンスを出したい場合は、orjsonをインストールし、ORJSONResponseをレスポンスとしてセットすることができます。

使いたい Response クラス (サブクラス) をインポートし、 path operationデコレータ に宣言します。

from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI()


@app.get("/items/", response_class=ORJSONResponse)
async def read_items():
    return [{"item_id": "Foo"}]

情報

パラメータ response_class は、レスポンスの「メディアタイプ」を定義するために利用することもできます。

この場合、HTTPヘッダー Content-Type には application/json がセットされます。

そして、OpenAPIにはそのようにドキュメントされます。

豆知識

ORJSONResponse は、現在はFastAPIのみで利用可能で、Starletteでは利用できません。

HTMLレスポンス

FastAPI からHTMLを直接返す場合は、HTMLResponse を使います。

  • HTMLResponse をインポートする。
  • path operation のパラメータ content_typeHTMLResponse を渡す。
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 を返す

レスポンスを直接返すで見たように、レスポンスを直接返すことで、path operation の中でレスポンスをオーバーライドできます。

上記と同じ例において、 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)

注意

path operation関数 から直接返される Response は、OpenAPIにドキュメントされず (例えば、 Content-Type がドキュメントされない) 、自動的な対話的ドキュメントからも閲覧できません。

情報

もちろん、実際の Content-Type ヘッダーやステータスコードなどは、返された Response オブジェクトに由来しています。

OpenAPIドキュメントと Response のオーバーライド

関数の中でレスポンスをオーバーライドしつつも、OpenAPI に「メディアタイプ」をドキュメント化したいなら、 response_class パラメータを使い、 Response オブジェクトを返します。

response_class はOpenAPIの path operation ドキュメントにのみ使用されますが、 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() は、str のHTMLを返すのではなく Response を生成して返しています。

generate_html_response() を呼び出した結果を返すことにより、FastAPI の振る舞いを上書きする Response が既に返されています。

しかし、一方では response_classHTMLResponse を渡しているため、 FastAPI はOpenAPIや対話的ドキュメントでHTMLとして text/html でドキュメント化する方法を知っています。

利用可能なレスポンス

以下が利用可能なレスポンスの一部です。

Response を使って他の何かを返せますし、カスタムのサブクラスも作れることを覚えておいてください。

技術詳細

from starlette.responses import HTMLResponse も利用できます。

FastAPI は開発者の利便性のために fastapi.responses として starlette.responses と同じものを提供しています。しかし、利用可能なレスポンスのほとんどはStarletteから直接提供されます。

Response

メインの Response クラスで、他の全てのレスポンスはこれを継承しています。

直接返すことができます。

以下のパラメータを受け付けます。

  • content - strbytes
  • 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レスポンスです。

UJSONResponse

ujsonを使った、代替のJSONレスポンスです。

注意

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ステータスコード (Temporary Redirect) となります。

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/typer")
async def read_typer():
    return RedirectResponse("https://typer.tiangolo.com")

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():
    file_like = open(some_file_path, mode="rb")
    return StreamingResponse(file_like, media_type="video/mp4")

豆知識

ここでは asyncawait をサポートしていない標準の open() を使っているので、通常の def でpath operationを宣言していることに注意してください。

FileResponse

レスポンスとしてファイルを非同期的にストリームします。

他のレスポンスタイプとは異なる引数のセットを受け取りインスタンス化します。

  • path - ストリームするファイルのファイルパス。
  • headers - 含めたい任意のカスタムヘッダーの辞書。
  • media_type - メディアタイプを示す文字列。セットされなかった場合は、ファイル名やパスからメディアタイプが推察されます。
  • filename - セットされた場合、レスポンスの Content-Disposition に含まれます。

ファイルレスポンスには、適切な Content-LengthLast-ModifiedETag ヘッダーが含まれます。

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)

デフォルトレスポンスクラス

FastAPI クラスのインスタンスか APIRouter を生成するときに、デフォルトのレスポンスクラスを指定できます。

定義するためのパラメータは、 default_response_class です。

以下の例では、 FastAPI は、全ての path operationJSONResponse の代わりに 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"}]

豆知識

前に見たように、 path operation の中で response_class をオーバーライドできます。

その他のドキュメント

また、OpenAPIでは responses を使ってメディアタイプやその他の詳細を宣言することもできます: Additional Responses in OpenAPI