FastAPIでExceptionを使用せずにHttpStatus200以外を返却する
始めに
FastAPIではHTTPException
を使用すればHttpStatusを200以外でも返却可能です。しかし、エラー詳細は1つしか返却できません。ファイルのエラーハンドリングでは多数のエラーが発生した場合には、複数のエラーを返却したいユースケースがあります。
ExceptionHandler
を使用することでもHTTPStatusや複数エラーを返却するようにできますが、基本的にはファイルアップロード部分の処理でしか使用しないため、全体に適用するには範囲が大きすぎます。
今回の記事ではファイルのバリデーション結果を返却するために次の仕様を満たせるようにします。
- エラーのBodyには複数の理由を設定できる
- バリデーションに失敗したらHTTPStatus200以外を返却
- OpenAPIにもレスポンス構造を反映できる
環境
- Python
- 3.12.4
- FastAPI
- 0.112.0
実装
レスポンスの型を定義する
HTTPException
をraiseすると次のレスポンス型で返却されます。
{
"detail": "FILE_INVALID"
}
今回はこのレスポンスを拡張した形でエラーレスポンスします。
{
"detail": "FILE_INVALID",
// ここから下を追加する形のレスポンスにする
"errorLists": {
"indexes": [
1,
2,
3
],
"reason": "Not Found"
}
}
具体的には次のような型定義をします。後でOpenAPI側でもデータ構造をわかるようにしたいので、examples
まで定義しておきます。
from typing import Any, List, Annotated
from pydantic import BaseModel, Field
class ErrorMessage(BaseModel):
reason: Annotated[str, Field(description="")]
indexes: Annotated[List[int], Field(description="エラーが発生したインデックスのリスト")]
class ExceptionResponse(BaseModel):
detail: Annotated[str, Field(description="Exception detail", examples=["File Invalid"])]
error_lists: Annotated[List[ErrorMessage], Field(description="Exception details", examples=[{"reason": "Not Found", "indexes": [1, 2, 3]}])]
HttpStatus 200以外で返却する
JSONResponse
を返却することで任意のHTTPStatusコードや任意の型定義を返却できます。
今回はファイルの内容が誤っていたことを伝えたいので、HTTPStatus=422で返却します。
return JSONResponse(
status_code=422,
content=ExceptionResponse(detail="File Invalid", error_lists=[]).model_dump(by_alias=True)
)
OpenAPIにもレスポンスの構造を伝える
APIRouter
のresponses
を定義するとOpenAPI側にレスポンスの型を伝えられます。そのため、先ほど定義したExceptionResponse
を指定します。
@router.get("/files", responses={422: {"model": ExceptionResponse}})
結果
OpenAPI側に定義を反映できます。
ソースコード
終わりに
ファイルのバリデーションは雑にやるならHTTPException
を返却するだけで済むのですが、だいたいユーザビリティが低いのでエラーをまとめなければならず、毎回実装するたびに悩んでいる気がします。
ファイルバリデーションのあるべきハンドリングの記事があればぜひ参考にしたいです。
Discussion