APIRouter を使った時のエラーハンドリング

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"  }

をレスポンスを返すようにしている。