LoginSignup
44
38

FastAPI 色々なレスポンスまとめ

Last updated at Posted at 2021-10-13

この記事は?

自分の備忘録も兼ねて、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-typetext/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-typetext/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_typeheaders で特に指定をしなかった場合、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 タイプ) を設定した上で、contentbytes 型データを入れると、バイナリデータを送ることができるようです。
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 ステータスコードが定数 (変数) で定義されています。

status.py
()
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

ステータスコードの意味が記述できるので、コードの可読性の向上につながるでしょう。

44
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
38