FastAPIで意図しないエラーが発生したときにException
でハンドリングしていましたが、それだけではpydantic
で発生するエラーがキャッチできなかったのでメモします。
なお、pydantic
はAPIのRequest
とResponse
のモデルで使用していますので、フロントのバリデーションエラーと考えてください。
環境
- Python
- 3.11
- FastAPI
- 0.105.0
- pydantic
- 2.5.3
ゴール
pydantic
で発生していた次の詳細すぎるメッセージが返却されないこと。
{ "detail": [ { "type": "greater_than_equal", "loc": [ "body", "age" ], "msg": "Input should be greater than or equal to 18", "input": 0, "ctx": { "ge": 18 }, "url": "https://errors.pydantic.dev/2.5/v/greater_than_equal" } ] }
実装
Exception
だけでなく、FastAPI
のRequestValidaionError
もException_handlerに追加する必要があります。
# 全てのエラーをキャッチするハンドラ # ただし、これだけではキャッチできないエラーも存在する @app.exception_handler(Exception) async def unhandled_exception_handler(request: Request, exc: Exception): return JSONResponse( status_code=500, content={"detail": f"SystemError"}, ) # 追記したハンドラ # pydanticのエラーをFastAPIでラップしたRequestValidationErrorを使用します。 @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): return JSONResponse( status_code=400, content={"detail": f"BadRequest"}, )
原因
FastAPI
側でハンドリングしていたから。
次の3つのエラーに関してはFastAPI
側で定義しています。
- HTTPException
- RequestValidationError
- WebSocketRequestValidationError
継承元のException
よりも継承先の特化したException
を定義しているので、特化したエラーが発生したときは特化したハンドリングが優先されます。FastAPI
を経由したテストを書かないと、エラーにひっかかるので気づくのに遅れました。
ソースコード
exception_handler
と対応したテストを書いてます。ただ、通常のExceptionが発生した場合は、Pytest上だとうまく確認できませんでした。
- https://github.com/hirotoKirimaru/fastapi-practice/blob/a5fbeccada45d9c03ca0ae2dabb92fc1634cab5b/src/main.py#L15-L27
- https://github.com/hirotoKirimaru/fastapi-practice/blob/a5fbeccada45d9c03ca0ae2dabb92fc1634cab5b/tests/scenario/test_exception.py
終わりに
最悪、url
をレスポンスしなければそのまま使用していいんですけどね。完全にpydanticを使ってます!
ってメッセージはちょっといただけないですね。
Exception
でハンドリングしているから大丈夫なはず、というので油断しました。今後も新しいフレームワークを使うときは、既存のexception_handlersを確認したほうが良いですね。