コンテンツへスキップ

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

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

Responseを直接返すで示されているように、Responseを直接返すことでオーバーライドできます。

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

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

パス操作関数から返されるコンテンツは、そのResponseの中に配置されます。

そして、そのResponseがJSONメディアタイプ(application/json)を持つ場合(JSONResponseUJSONResponseの場合のように)、返されるデータは、パス操作デコレータで宣言したPydanticのresponse_modelによって自動的に変換(およびフィルタリング)されます。

Note

メディアタイプのないレスポンスクラスを使用すると、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-Typeapplication/jsonに設定されます。

そして、それはOpenAPIでそのように文書化されます。

ヒント

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

HTMLレスポンス

FastAPIから直接HTMLを含むレスポンスを返すには、HTMLResponseを使用します。

  • HTMLResponseをインポートします。
  • HTMLResponseパス操作デコレータresponse_classパラメータとして渡します。
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-Typetext/htmlに設定されます。

そして、それはOpenAPIでそのように文書化されます。

Response を返す

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)

Warning

パス操作関数によって直接返される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が既に返されています。

しかし、HTMLResponseresponse_classにも渡しているので、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 orjsonorjsonをインストールする必要があります。

UJSONResponse

ujsonを使用した代替JSONレスポンス。

情報

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

Warning

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_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 を使用する

ファイルライクオブジェクト(例: 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")
  1. これがジェネレータ関数です。yieldステートメントが含まれているため、「ジェネレータ関数」と呼ばれます。
  2. withブロックを使用することで、ジェネレータ関数の完了後(つまり、レスポンスの送信が完了した後)にファイルライクオブジェクトが閉じられることを保証します。
  3. このyield fromは、関数にfile_likeというものを反復処理するように指示します。そして、反復処理された各部分について、その部分をこのジェネレータ関数(iterfile)から来たものとして生成します。

    つまり、内部的に「生成」の仕事を他のものに転送するジェネレータ関数です。

    この方法で実行することで、それをwithブロックに入れることができ、そのようにして、完了後にファイルライクオブジェクトが閉じられることを保証します。

ヒント

ここではasyncawaitをサポートしない標準のopen()を使用しているため、パス操作を通常のdefで宣言していることに注意してください。

FileResponse

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

他のレスポンスタイプとは異なる引数セットでインスタンス化されます。

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

ファイルレスポンスには、適切なContent-LengthLast-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における追加のレスポンス