この記事は?
自分の備忘録も兼ねて、FastAPI のレスポンスについてまとめました。FastAPI の基本的な記述方法、HTTP の基礎的な知識については、ここでは説明していません。チートシート的なものとしてご活用ください。
以下のドキュメントを元に (というか、ほぼコレの二番煎じを) 作成しました。
(6/27 追記) リクエストの受信についてはこちら:
JSON レスポンス (単純な方法)
FastAPI では、デフォルトでは JSON 形式のレスポンスをするようになっています。return
に辞書オブジェクトを配置すると、JSON に変換されてレスポンスされます。
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def index():
return {"mes": "hello"}
プレーンテキストを記述しても、MIME タイプは application/json
としてレスポンスされてしまうので注意。
@app.get("/")
def index():
return "hello"
# クライアントには "content-type: application/json" で届く
MIME タイプを調整した上でレスポンスするには、後述の方法を利用。
JSON レスポンス (丁寧な方法)
FastAPI では Response
オブジェクトを return
することで、ヘッダやステータスコード等を指定したレスポンスをすることができます。
Response
を継承したクラスで JSONResponse
等があり、これらは各種レスポンスに対応した初期値が設定されています。例えば JSONResponse
の場合、ヘッダに初期値として content-type: application/json; chareset: utf-8
が指定されています。
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/")
def index():
return JSONResponse(content={"mes": "hello"})
# 最初の引数に指定時 content= 省略可, 他レスポンスも同様
# 例: return JSONResponse({"mes": "hello"})
同様の働きをするもので ORJSONResponse
があり、ドキュメントによるとこちらの方が高速らしいです。UJSONResponse
というクラスもありますが、ドキュメントを見る限り使う意味は無さそうです。
HTML レスポンス (直書き)
デフォルトの content-type
は text/html
です。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/")
def index():
return HTMLResponse(content="<h1>hello</h1>")
HTML レスポンス (Jinja2Template 使用)
十分大規模な HTML を返す場合、直書きはソースコードの可読性を落とすので良くありません。別ファイルから HTML を読み込んでレスポンスをする場合、Jinja2Template を使うのがスマートです。
Jinja2Template を使用するには、pip からインストールが必要です。
pip install jinja2
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI(request: Request)
templates = Jinja2Templates(directory="templates")
@app.get("/")
def index(request: Request):
return templates.TemplateResponse("page.html", {"request": request})
PlainTextResponse
デフォルトの content-type
は text/plain
です。
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.get("/")
def index():
return PlainTextResponse(content="hello")
RedirectResponse
ステータスコードは、デフォルトで 307 (Temporary Redirect) が設定されます。
必要に応じ、別のステータスコードを指定します。
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/")
def index():
return RedirectResponse(url="http://example.com")
# 最初の引数に指定時 url= 省略可
# status_code= でステータスコード指定
# 例: return RedirectResponse("http://example.com", status_code="308")
FileResponse
media_type
や headers
で特に指定をしなかった場合、content-type
は推測されて設定されます。
from fastapi import FastAPI
from fastapi.responses import FileResponse
app = FastAPI()
@app.get("/")
def index():
return FileResponse(path="path-to/file.txt", media_type="text/plain")
# 最初の引数に指定時 path= 省略可
# media_type の代わりにヘッダを自力で記述することも可
# 例: return FileResponse("path-to/file.txt", headers={"content-type": "text/plain; charset: utf-8"})
カスタムした任意のレスポンス
Response
クラスで、ヘッダ等の初期値が指定されていない状態のレスポンスオブジェクトを作成できます。
from fastapi import FastAPI
from fastapi.responses import Response
app = FastAPI()
@app.get("/")
def index():
res = Response(content="hello",
media_type="text/plain",
#headers={"content-type": "text/plain; charset: utf-8"},
# ヘッダを自力で記述しても良い
status_code=200)
return res
適切な media_type
(MIME タイプ) を設定した上で、content
に bytes
型データを入れると、バイナリデータを送ることができるようです。
FileResponse
と違い、ディスク上のファイルでなく、変数に入れられているファイル (バイナリデータ) を送ることができます。
その他メモ
レスポンスクラスの指定方法
書き方1: response_class
を指定
return
には、直接ボディの内容を書きます。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
def index():
return """
<h1>hello</h1>
"""
書き方2: Response
オブジェクトを return
本記事で書いてきた方法です。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/")
def index():
return HTMLResponse(content="<h1>hello</h1>")
インスタンス化後に値の変更
Response
オブジェクトの作成で、インスタンス化するときに各値を指定する方法と、インスタンス化後に各値を設定する方法があります。
# ※※※ 推奨できない書き方 ※※※
@app.get("/")
def index():
res = Response()
res.body = b"hello"
res.raw_headers = [(b'content-type', b'text/plain; charset: utf-8')]
res.status_code = 200
return res
後者の方法 (上記ソースコードの方法) はあまり推奨できません。理由としては・・・
-
content-length
等のデータは、インスタンス化時に自動で設定してくれる - ヘッダとボディのデータは
bytes
変換しないと代入できないため、書き方が面倒
デフォルトのレスポンスクラスの指定
デフォルトでは JSON がレスポンスされるようになっていますが、FastAPI
をインスタンス化する際に、デフォルトのレスポンスクラスを指定することができます。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI(default_response_class=HTMLResponse)
@app.get("/")
def index():
return "<h1>hello</h1>"
ステータスコード指定の書き方
FastAPI には status
というモジュールが用意されており、その中には各種 HTTP ステータスコードが定数 (変数) で定義されています。
(略)
HTTP_200_OK = 200
HTTP_201_CREATED = 201
HTTP_202_ACCEPTED = 202
HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203
(略)
from fastapi import FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/")
def index():
res = JSONResponse(content={"mes": "hello"},
status_code=status.HTTP_200_OK)
return res
ステータスコードの意味が記述できるので、コードの可読性の向上につながるでしょう。