はじめに
この記事では、通常、クライアントのリクエストにおけるバリデーションエラーを示すデフォルトの422エラーをFastAPIでカスタマイズする方法を紹介します。主に次の2つのタスクを説明します。
- RequestValidationErrorのデフォルトの例外ハンドラをオーバーライドして、カスタムのレスポンスを定義
- OpenAPIドキュメントを変更して、デフォルトの422エラーレスポンスを削除
FastAPIでのデフォルトの422エラーのカスタマイズ
以下では、FastAPIでデフォルトの422エラーをカスタマイズする手順について説明します。これには、RequestValidationErrorのデフォルトの例外ハンドラをオーバーライドし、カスタムのバリデーションエラーレスポンスを作成する必要があります。
デフォルトの例外ハンドラのオーバーライド
RequestValidationErrorのデフォルトの例外ハンドラをオーバーライドするには、次の手順に従ってください。
- 必要なモジュールをインポートし、新しいFastAPIアプリケーションインスタンスを作成
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
app = FastAPI()
- カスタムの例外ハンドラ関数を定義
カスタムの例外ハンドラ関数はRequestオブジェクトと発生したRequestValidationError例外を受け取る必要があります。関数は、JSONResponseなどのカスタムレスポンスを返すようにする必要があります。
async def custom_request_validation_exception_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=422,
        content={
            "error_code": "CUSTOM_VALIDATION_ERROR",
            "message": "One or more request parameters failed validation",
            "detail": exc.errors(),
        },
    )
- カスタムの例外ハンドラをFastAPIアプリケーションインスタンスに登録
app.exception_handler(RequestValidationError)(custom_request_validation_exception_handler)
カスタムのバリデーションエラーレスポンスの作成
カスタムのバリデーションエラーレスポンスを作成するには、次の手順に従ってください。
- Pydanticを使用してカスタムのレスポンスモデルを定義
カスタムのレスポンスモデルは、カスタムエラーレスポンスで返すフィールドを含める必要があります。
from pydantic import BaseModel
class CustomValidationError(BaseModel):
    error_code: str
    message: str
    detail: List[Dict[str, Any]]
- カスタムの例外ハンドラ関数を更新して、カスタムのレスポンスモデルを使用するようにする
async def custom_request_validation_exception_handler(request: Request, exc: RequestValidationError):
    custom_error = CustomValidationError(
        error_code="CUSTOM_VALIDATION_ERROR",
        message="One or more request parameters failed validation",
        detail=exc.errors(),
    )
    return JSONResponse(status_code=422, content=custom_error.dict())
これらの手順に従うことで、FastAPIでデフォルトの422エラーをカスタマイズすることができます。RequestValidationErrorが発生した場合には、カスタムのエラーレスポンスが返されます。
OpenAPIドキュメントからデフォルトの422エラーを削除
FastAPIは自動的にAPIのためのOpenAPIドキュメントを生成します。このドキュメントには、エンドポイントからの可能なレスポンスに関する情報が含まれます。デフォルトでは、OpenAPIドキュメントには、バリデーションエラーが発生する可能性を示すために、全てのエンドポイントに対して422エラーレスポンスが含まれます。
一部の場合では、OpenAPIドキュメントからこのデフォルトの422エラーを削除したい場合があります。これは、app.openapi()メソッドをカスタマイズすることで行うことができます。以下にその方法を示します。
- FastAPIアプリケーションインスタンスを定義した後、app.openapi()メソッドをオーバーライド
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}
def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="Custom schema",
        version="2.5.0",
        description="This is a very custom OpenAPI schema",
        routes=app.routes,
    )
    openapi_schema["paths"].pop("/openapi.json")
    app.openapi_schema = openapi_schema
    return app.openapi_schema
app.openapi = custom_openapi
上記のコードでは、custom_openapi()関数は既存のOpenAPIスキーマが存在する場合はそれを取得し、存在しない場合は新しいスキーマを生成します。openapi_schema["paths"].pop("/openapi.json")の行は、スキーマからOpenAPI JSONルートを削除します。
- custom_openapi()関数を更新して、OpenAPIスキーマからデフォルトの422エラーを削除
def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="Custom schema",
        version="2.5.0",
        description="This is a very custom OpenAPI schema",
        routes=app.routes,
    )
    for path, path_dict in openapi_schema["paths"].items():
        for method, method_dict in path_dict.items():
            method_dict.pop("responses", {}).pop("422", None)
    app.openapi_schema = openapi_schema
    return app.openapi_schema
上記の更新されたcustom_openapi()関数では、OpenAPIスキーマ内の全てのパスとメソッドをイテレートし、レスポンスに422が存在する場合にそれを削除します。