この記事を読んでできること
- エラーハンドリングができる
- 同じような例外処理を各APIに書かなくても済む(可読性・保守性の向上)
概要
StarletteのBaseHTTPMiddlewareを使用して共通した複数の例外処理の機能を受け持つようにする
記述しないこと
- FastAPIについて
- 例外処理について
使用技術
- Python 3.10.4
- fastapi 0.78.0
- Starlette 0.19.1
ミドルウェアの処理の流れ
- アプリケーションに届いたリクエストをミドルウェアが受け取る
- そのリクエストに対して何らかのコードを実行する
- アプリケーションの残りの部分にリクエストを渡して (path operation によって) 処理させる
- ミドルウェアはアプリケーション (の path operation) によって生成されたレスポンスを受け取る
- そのレスポンスに対して何らかのコードを実行する
- レスポンスを返す
実装例
main.py
from fastapi import FastAPI
from middleware import ErrorHandlingMiddleware
app = FastAPI()
app.add_middleware(ErrorHandlingMiddleware)
アプリケーションにミドルウェアを呼び込むにはapp.add_middleware()
を追記する
error.py
class InvalidInputError(Exception):
"""APIにて受付番号が発行されなかった場合のカスタムエラー"""
pass
自作エラーの設定
middleware.py
from fastapi import Request, Response, status
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from error import InvalidInputError
class ErrorHandlingMiddleware(BaseHTTPMiddleware):
"""エラーハンドリングをするミドルウェア
API内で発生したエラーをキャッチして処理を施す
Args:
BaseHTTPMiddleware : リクエスト/レスポンスインタフェースに対するASGIミドルウェアを記述するための抽象クラス
"""
async def dispatch(self, request: Request, call_next) -> Response:
try:
response: Response = await call_next(request)
except InvalidInputError as e:
response = JSONResponse(
{"msg": "InvalidInputError:受付番号が発行されませんでした。送信内容をもう一度確認してください。"},
status.HTTP_500_INTERNAL_SERVER_ERROR,
)
except TypeError as e:
response = JSONResponse(
{"msg": "TypeError:内容を確認してもう一度データ挿入をしてください。"},
status.HTTP_404_NOT_FOUND,
)
except TimeoutError as e:
response = JSONResponse(
{"msg": "TimeoutError:タイムアウトエラーが発生しました。"},
status.HTTP_408_REQUEST_TIMEOUT,
)
except RuntimeError as e:
response = JSONResponse(
{"msg": "RuntimeError:ランタイムエラーが発生しました。"},
status.HTTP_500_INTERNAL_SERVER_ERROR,
)
except Exception as e:
response = JSONResponse(
{"msg": "Exception:基底クラスエラーが発生しました。"},
status.HTTP_500_INTERNAL_SERVER_ERROR,
)
return response
- APIで発生したエラーは最終的にこのミドルウェアでキャッチしてエラーメッセージを返すことができる
- 自作エラーも使用することができる
- エラー以外にも共通する処理であればなんでもできる
まとめ
ミドルウェアってすごい便利だね