0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FastAPIのPOSTリクエストで発生する422エラーの原因と解決法

0
Posted at

FastAPIでPOSTリクエストを投げた際、なぜか422 Unprocessable Entityエラーが返ってくる現象に遭遇した。DBへのデータ投入や処理の前にバリデーションエラーで弾かれてしまうと、開発が止まってしまいがちだ。

このエラー、実はFastAPIのPydanticモデルがクライアントから送られてきたJSONを正しく解釈できなかった場合に発生する。原因を特定するのが意外と面倒で、ハマると時間を食うので、現場で役立つ解決策をまとめることにした。

※この記事は、個人技術ブログ CodeArchPedia.com の技術メモ(要約)です。

何が起きたか(課題)

FastAPIのPOSTエンドポイントで、期待通りのJSONを送信しているにもかかわらず、サーバー側で422エラーが返却された。具体的な問題点は以下の通りだった。

  • リクエストボディのフィールド名が、Pydanticモデルの定義と一致していなかった(特にキャメルケースとスネークケースの混同)。
  • 必須フィールドが欠落している、またはデータ型が期待値と異なっていた(例:文字列が送られるべきところに数値が入るなど)。
  • エラーレスポンスが詳細すぎて、フロントエンドでのハンドリングがしづらい。

どう解決したか(概要)

解決の基本方針は、FastAPIが自動で返すエラーレスポンスのdetail情報を徹底的に読み解くことだった。これにより、どのフィールドの、どのような型の不整合が発生しているのかを正確に把握できる。

具体的には、以下のステップで対応を進めた。

  1. エラーレスポンスのloc(場所)とmsg(内容)を確認し、Pydanticモデル定義とリクエストJSONを突合した。
  2. フロントエンドでcamelCaseを使っている場合、PydanticのField(alias='...')を使用してAPIの口(JSONキー)と内部の変数名(Pythonの変数名)を分離した。
  3. 開発者向けの詳細エラーではなく、API利用者に分かりやすいシンプルなエラーを返すため、RequestValidationErrorを捕捉してカスタムハンドラーを実装した。

例えば、以下のようなPydanticモデルでエラーが発生した場合、

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float

"price": "1000"のような文字列を送ると、エラーメッセージからpriceフィールドでfloat型エラーが発生していることが特定できる。

422エラーの主要な原因

FastAPIはPydanticモデルに基づいて自動でバリデーションを実行する。原因は主に以下の3つに分類される。

  • Pydanticモデルとの不整合: 必須フィールドの欠落、データ型の不一致、フィールド名のタイポ。
  • 不正なリクエスト形式: Content-Type: application/jsonヘッダーの設定漏れや、JSON自体の構文エラー。

FastAPIのエラー詳細を活用したデバッグ

エラー発生時のレスポンス構造がデバッグの鍵を握る。例えば、数値のフィールドに文字列を送った場合、以下のようなレスポンスが返ってくる。

{
  "detail": [
    {
      "loc": [
        "body",
        "price"
      ],
      "msg": "value is not a valid float",
      "type": "type_error.float"
    }
  ]
}

この情報から、エラーがリクエストボディのpriceフィールドで発生しており、float型として不正であると即座に判断できた。

エラー原因 不正なリクエスト例 (JSON) 正しいリクエスト例 (JSON)
必須フィールドの欠落 { "name": "My Item" } { "name": "My Item", "price": 9.99 }
データ型の不一致 { "name": "My Item", "price": "9.99" } { "name": "My Item", "price": 9.99 }
フィールド名のタイポ { "Name": "My Item", "price": 9.99 } { "name": "My Item", "price": 9.99 }

現場で役立つ実践的なTips

エイリアスを使ったキャメルケース対応

フロントエンドとの連携でcamelCaseを受け入れるため、Pydanticのエイリアス機能を利用した。これにより、クライアントからの{"userId": 1}というリクエストを、Pythonコード内ではuser.user_idとして扱えるようになる。

RequestValidationErrorのカスタム

デフォルトのエラーレスポンスを、API利用者が扱いやすい形に整形した。エラーロケーションをドット区切りにし、メッセージを簡潔にまとめることで、フロントエンドのバリデーションハンドリングが容易になった。

効果(Before/After)

この一連の対応を行った結果、バリデーション起因のエラー特定時間が大幅に短縮された。以前は原因不明のままコードを修正していたのが、今ではエラーメッセージを元に数分で特定可能になった。特にエイリアス設定とカスタムエラーハンドリングにより、フロントエンドとの認識齟齬がほぼ解消されたのが大きな成果だ。


🚀 詳細な設定とコードはこちら

具体的なWAFのルール設定や、より詳細なログ解析データは元のブログで公開しています。

👉 FastAPIのPOSTリクエストで発生する422エラーの原因と解決法

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?