コンテンツにスキップ

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

🌐 Translation by AI and humans

This translation was made by AI guided by humans. 🤝

It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖

You can improve this translation by helping us guide the AI LLM better.

English version

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

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

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

response_class パラメータを使用して、path operation デコレータ で使用したい Response(任意の Response サブクラス)を宣言することもできます。

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

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

備考

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

ORJSONResponse を使う

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

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

大きなレスポンスの場合、Response を直接返すほうが、辞書を返すよりもはるかに高速です。

これは、デフォルトではFastAPIがチュートリアルで説明した同じJSON Compatible Encoderを使って、内部の各アイテムを検査し、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 をインポートする。
  • path operation デコレータ のパラメータ response_classHTMLResponse を渡す。
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_class にも HTMLResponse を渡しているため、FastAPI はOpenAPIと対話的ドキュメントで、text/html のHTMLとしてどのようにドキュメント化すればよいかを理解できます:

利用可能なレスポンス

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

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

技術詳細

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

FastAPI は開発者の利便性のために、starlette.responses と同じものを fastapi.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レスポンスです。

情報

これは、例えば pip install orjsonorjson をインストールする必要があります。

UJSONResponse

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

情報

これは、例えば pip install ujsonujson をインストールする必要があります。

注意

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)となります。

RedirectResponse を直接返せます:

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


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

または、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.tiangolo.com"

その場合、path operation関数からURLを直接返せます。

この場合に使用される status_codeRedirectResponse のデフォルトである 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 を使う

file-like オブジェクト(例: open() で返されるオブジェクト)がある場合、そのfile-likeオブジェクトを反復処理するジェネレータ関数を作れます。

そうすれば、最初にすべてをメモリへ読み込む必要はなく、そのジェネレータ関数を 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")
  1. これはジェネレータ関数です。内部に yield 文を含むため「ジェネレータ関数」です。
  2. with ブロックを使うことで、ジェネレータ関数が終わった後(つまりレスポンスの送信が完了した後)にfile-likeオブジェクトが確実にクローズされるようにします。
  3. この yield from は、file_like という名前のものを反復処理するように関数へ指示します。そして反復された各パートについて、そのパートをこのジェネレータ関数(iterfile)から来たものとして yield します。

    つまり、内部的に「生成」の作業を別のものへ移譲するジェネレータ関数です。

    このようにすることで with ブロックに入れられ、完了後にfile-likeオブジェクトが確実にクローズされます。

豆知識

ここでは 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)

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

この場合、path operation関数からファイルパスを直接返せます。

カスタムレスポンスクラス

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 はすべてのpath operationで、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"}]

豆知識

これまでと同様に、path operationresponse_class をオーバーライドできます。

その他のドキュメント

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