FastAPI で、APIRouter を使った時のバリデーションエラーハンドリングを
メインのスクリプトで FastAPI() で、include_router(router: APIRouter) するのと同時に
FastAPI の exception_handler を書く方法だと、APIRouter で折角、ルート毎に書いていても
1箇所でのエラーハンドラ定義で、全てのバリデーションエラーを処理するものを
書かなくてはならない。
APIRouter を書いているスクリプト毎に、各々細かいエラーハンドリングを書いて対応させたいのだが、
APIRouter に、 def exception_handler は存在しない。
どうしたらルート毎に作れるか?
参考になったのが、以下、class APIRoute が持っている get_route_handler を継承して、
APIRouter の route_class として指定する方法だ。
how to use customer exception handler by APIRouter? · tiangolo fastapi · Discussion #7323 · GitHub
これを参考に以下のように、APIRouter 生成をするスクリプトで、
バリデーションエラーを捕捉するハンドラを書き、正常時、他のエラーは通常の対応の処理にする。
from fastapi import APIRouter, Request, status, Response from fastapi.routing import APIRoute, Callable from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse class PrometheusRoute(APIRoute): def get_route_handler(self) -> Callable: original_route_handler = super().get_route_handler() async def custom_route_handler(request: Request) -> Response: app = request.app try: return await original_route_handler(request) except RequestValidationError as exc: print(exc) excs = eval(exc.__str__()) # TODO バリデーションエラー状況に対応した処理 return JSONResponse(content={"errormesage":excs[0].get('msg')}, status_code=status.HTTP_400_BAD_REQUEST) except Exception as exc: app.logger.warning('{}'.format(exc.detail)) raise exc return custom_route_handler router = APIRouter(route_class=PrometheusRoute)
上の例では、RequestValidationError が、
[{'type': 'less_than', 'loc': ('query', 'id'), 'msg': 'Input should be less than 256', 'input': '256', 'ctx': {'lt': 256}}]
である時、HTTPステータス 400 の Bad Request として、
{ "errormesage": "Input should be less than 256" }
をレスポンスを返すようにしている。