バリデーション3
バリデーションエラーをユーザーに返す際、最も重要なのは**「どこが(Where)」「なぜ(Why)」間違っているかを、機械ではなく人間が理解できる言葉で伝えること**です。
PydanticとFastAPIなどのモダンな構成を例に、具体的なエラーハンドリングの実装パターンを紹介します。
1. 良いエラーレスポンスの構造¶
APIから返すエラーは、以下のような構造にするとフロントエンド(JavaScriptなど)側で処理しやすくなり、ユーザーへの表示もスムーズになります。
JSON
{
"status": "error",
"message": "入力内容に不備があります。",
"details": [
{
"field": "quantity",
"reason": "must_be_positive",
"message": "数量は1以上にしてください。"
},
{
"field": "contact_email",
"reason": "invalid_format",
"message": "有効なメールアドレスを入力してください。"
}
]
}
2. Pythonでの実装例(Pydanticのエラーを変換)¶
Pydanticが発生させるエラー(ValidationError)をそのまま返すと、英語のメッセージや内部的なパスが含まれてしまい、一般ユーザーには不親切です。これを「翻訳・整形」するロジックをスタブ的に作成します。
Python
from pydantic import ValidationError
def format_pydantic_errors(e: ValidationError):
"""
Pydanticのエラーオブジェクトを、フロントエンド用のわかりやすい辞書形式に変換する
"""
error_details = []
for error in e.errors():
# error['loc'] は ('quantity',) のようなタプル
field_name = error['loc'][0]
# エラータイプに応じて日本語メッセージを割り当て(簡易版)
raw_msg = error['msg']
friendly_msg = "入力内容が正しくありません"
if "greater than 0" in raw_msg:
friendly_msg = "1以上の数字を入力してください。"
elif "value is not a valid email" in raw_msg:
friendly_msg = "メールアドレスの形式が正しくありません。"
elif "field required" in raw_msg:
friendly_msg = "この項目は必須です。"
error_details.append({
"field": field_name,
"message": friendly_msg
})
return {
"status": "validation_error",
"errors": error_details
}
3. ユーザーインターフェース(UI)への反映¶
バックエンドから返ってきたエラーメッセージを、画面上のどこに表示するかが重要です。
- インライン表示: 各入力項目のすぐ下に赤い文字で表示する(最も推奨)。
- トースト通知: 画面の隅に一時的に「保存に失敗しました」と出す(補助的)。
- サマリー表示: フォームの最上部にエラー内容を箇条書きで出す。
4. 5,000行規模での「エラーハンドリング」の工夫¶
この規模のプロジェクトでは、エラー処理を各所にバラバラに書くとコードが肥大化します。
- 共通例外クラスの作成:
AppErrorのような独自のベースクラスを定義する。 - グローバルなエラーキャッチャー: Webフレームワークの機能(Middlewareなど)を使い、どこでエラーが起きても同じ形式のレスポンスが返るように共通化する。
- 多言語対応(i18n): メッセージをコード内に直接書かず、
messages.yamlなどの外部ファイルに切り出す。
次のステップへの提案¶
エラーハンドリングを共通化する**「ミドルウェア(Middleware)」や「例外ハンドラ(Exception Handler)」の具体的な書き方**について興味はありますか?
あるいは、実際のアプリ開発において、「エラーが起きたことをログに残して開発者に通知する方法」(Sentryなどのツール活用)についてお話ししましょうか?