FastAPI HTTP 422 エラーの時のレスポンスを定義する。 - Oboe吹きプログラマの黙示録
の方法や、
APIRouter を使った時のエラーハンドリング - Oboe吹きプログラマの黙示録
の方法でも、
RequestValidationError のレスポンスパターンに沿って返すメッセージを
1つの設定ファイルでまとめて管理する。
バリデーションエラーは、以下のようなエラーが発生する
JSONエラー:要求するリクエストJSONが、JSON書式でないなど無茶苦茶なリクエストの時、、
[{'type': 'json_invalid', 'loc': ('body', 18), 'msg': 'JSON decode error', 'input': {}, 'ctx': {'error': "Expecting ',' delimiter"}}]
必須エラー:type が、missing になる
[{'type': 'missing', 'loc': ('body', 'name'), 'msg': 'Field required', 'input': {'price': 200, 'createdate': '2024-04-13', 'createtime': '2024-04-19 21:32:09'}}]
型や範囲のエラー
[{'type': 'int_parsing', 'loc': ('path', 'id'), 'msg': 'Input should be a valid integer, unable to parse string as an integer', 'input': '12a'}] [{'type': 'less_than', 'loc': ('path', 'id'), 'msg': 'Input should be less than 12', 'input': '12', 'ctx': {'lt': 12}}] [{'type': 'greater_than', 'loc': ('path', 'id'), 'msg': 'Input should be greater than 0', 'input': '0', 'ctx': {'gt': 0}}]
enum に対するパターンエラー
[{'type': 'enum', 'loc': ('path', 'modelname'), 'msg': "Input should be 'Red', 'Green', 'Blue' or 'Yellow'", 'input': 'Yellowp', 'ctx': {'expected': "'Red', 'Green', 'Blue' or 'Yellow'"}}]
日付範囲エラー
[{'type': 'value_error', 'loc': ('body', 'createdate'), 'msg': 'Value error, Invalid createdate >= 2024-4-1', 'input': '2024-03-24', 'ctx': {'error': ValueError('Invalid createdate >= 2024-4-1')}}]
日付書式エラー
[{'type': 'date_from_datetime_parsing', 'loc': ('body', 'createdate'), 'msg': 'Input should be a valid date or datetime, input is too short', 'input': '2024-0324', 'ctx': {'error': 'input is too short'}}] [{'type': 'datetime_from_date_parsing', 'loc': ('body', 'createtime'), 'msg': 'Input should be a valid datetime or date, unexpected extra characters at the end of the input', 'input': '2024-04-1921:32:09', 'ctx': {'error': 'unexpected extra characters at the end of the input'}}]
パスパラメータの異常であれば、loc タプルの [0] は、'path'
クエリやJSONパラメータであれば、loc タプルの [0] は、'body'
loc タプルの [1] が、エラーになったパラメータ名になる。
この規則にそって、エラーを解析して当てはまるパラメータに沿った
以下のような YAML をエラーメッセージを定義する YAML を用意する。
全てのバリデーションエラーを「リクエストが不正です。」の一言のメッセージを返すことが
許されるならこんな YAML を用意して対応させる必要はないのだが、細かいエラー理由のメッセージを
要求されることが要件がほとんどである。
errormessage.yaml
# エラーメッセージ定義 /query: id: default : 'パラメータ id は、0 から 255 以下の値でなければなりません' missing : 'パラメータ id は、必須です' meta: default : 'パラメータ meta は、2 文字以上、12文字以内でなければなりません' /add: name: default: 'パラメータ name は、文字列でなければなりません' missing: 'パラメータ name は、必須です' price: default: 'パラメータ price は、100より大きい値でなければなりません' missing: 'パラメータ price は、数値で必須です' createdate: default: 'パラメータ createdate は、書式 %Y-%m-%d の日付でなければなりません' createtime: default: 'パラメータ createtime は、書式 %Y-%m-%d %H:%M:%S の日付でなければなりません'
このYAMLを読み込んでバリデーションエラー発生時のレスポンスを
exception_handler で組み立てるには、、
from fastapi import FastAPI, Request, status from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse import yaml import codecs with codecs.open('errormessage.yaml', 'r', 'utf-8') as file: errconfig = yaml.load(file, Loader=yaml.SafeLoader) app = FastAPI() @app.exception_handler(RequestValidationError) async def handler(request:Request, exc:RequestValidationError): excs = eval(exc.__str__()) if excs[0].get('type') == 'json_invalid': errmsg = 'リクエストが不正です' elif excs[0].get('type') == 'missing': if request.url.path in errconfig: d = errconfig[request.url.path].get(excs[0].get('loc')[1]) errmsg = d.get('missing', d.get('default', 'Unknown Error')) else: errmsg = "Unknown Error" else: if excs[0].get('loc')[0] == 'path': errmsg = f'パスパラメータ {excs[0].get('loc')[1]} は指定が誤っています。' else: if request.url.path in errconfig: d = errconfig[request.url.path].get(excs[0].get('loc')[1]) errmsg = d.get('default', 'Unknown Error') return JSONResponse(content={"errormesage":errmsg}, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)
これで、必須エラーの場合以下のレスポンスを返すことができる。
{ "errormesage": "パラメータ price は、数値で必須です" }
FastAPI HTTP 422 エラーの時のレスポンスを定義する。 - Oboe吹きプログラマの黙示録
で作成する APIRoute 継承の def get_route_handler でも
RequestValidationError 捕捉時にこれと同様を書けば良いであろう。