FastAPIのバリデーションエラーのレスポンスメッセージをYAMLで

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 捕捉時にこれと同様を書けば良いであろう。