python

【Python】FastAPIでエラースキーマを定義しつつ、自作ハンドラーで例外処理する

今回はFastAPIのスキーマ定義を利用してエラースキーマを定義しつつ、自作ハンドラーで処理する方法をまとめました。

サンプルコード

以下、スキーマ定義と例外ハンドラーを実装したものになります。

import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel

app = FastAPI()


class MyErrorHandler(Exception):
    """例外ハンドラー"""

    def __init__(self, detail: str) -> None:
        self.detail = detail


class APIError(BaseModel):
    """エラースキーマ"""

    detail: str


@app.get(path="/hello")
def hello_world():
    return {"hello": "world"}


@app.get("/user")
def get_user():
    raise MyErrorHandler(detail="処理に失敗しました")


@app.exception_handler(MyErrorHandler)
def api_error(req: Request, exc: MyErrorHandler) -> JSONResponse:
    api_exc = APIError(detail=exc.detail)
    return JSONResponse(content=api_exc.dict(), status_code=500)


def main():
    uvicorn.run("main:app", port=8000, host="0.0.0.0", reload=True)


if __name__ == "__main__":
    main()

解説していきます。

例外ハンドラーを定義

例外ハンドラーについては公式ドキュメントがわかりやすいです。

https://fastapi.tiangolo.com/ja/tutorial/handling-errors/

今回はExpectionを継承して自作の例外ハンドラーを作成しました。

class MyErrorHandler(Exception):
    """例外ハンドラー"""

    def __init__(self, detail: str) -> None:
        self.detail = detail

引数にdetailを受け取り、エラーメッセージを保管します。

エラースキーマを定義

スキーマは通常、レスポンススキーマを定義するために使用されます。

https://fastapi.tiangolo.com/ja/tutorial/response-model/

これを応用してエラーレスポンスにもスキーマ定義ができないか試してみました。

class APIError(BaseModel):
    """エラースキーマ"""

    detail: str

簡単な感じですがエラースキーマを定義しました。

処理的には先程のMyErrorHandlerが保管しているdetailフィールドをこのスキーマが受け取るようにする形です。

使用してみる

今回は任意のエンドポイントで例外が発生するようにしてみました。

@app.get("/user")
def get_user():
    raise MyErrorHandler(detail="処理に失敗しました")

これだと、/userにアクセスした瞬間に自作ハンドラーで受けてレスポンスを返すようになります。

実際に試してみる

実際に試してみます。

FastAPIはSwagger UIを自動生成してくれるので立ち上げたらhttp:localhost:8000/docsにアクセスします。

この画面で/userでリクエストを投げてみます。

すると、Response bodyのところに自分で作成したスキーマがちゃんと返却されているのが確認できます。

@app.exception_handler(MyErrorHandler)
def api_error(req: Request, exc: MyErrorHandler) -> JSONResponse:
    api_exc = APIError(detail=exc.detail)
    return JSONResponse(content=api_exc.dict(), status_code=500)

このコードの最後で返却しています。

JSONResponse(content=)のところでスキーマ指定します。

この時、dict()メソッドで型変換して返却しないとエラーになるので気をつけてください。

まとめ

今回はFastAPIのスキーマ定義を利用してエラースキーマを定義しつつ、自作ハンドラーで処理する方法をまとめました。

APIでは様々な例外発生条件があるため、自作ハンドラーを作成しておく方法を知っておくと後々役立つかもしれません。

今回は以上です。