私が遭遇した422エラー(FastAPI)


ローカル環境ではapiリクエストをテストするためにPostManからFastAPIサーバにhttpリクエストを送信し,次のような戻り値を得た.
{
    "result": null,
    "processTime": 0.011934995651245117,
    "message": "Unprocessable Entity",
    "code": 422
}
422 Unprocessable Entity
  • MDN Web Docsを参照すると、次のような答えが得られます.
    この応答は、サーバが要求を理解し、要求構文が正しいが、要求の指示に従うことができないことを示します.
  • サーバも私のリクエストを受けました...文法も正しくありませんが、要求された指示に従うことはできません...?
  • 私が経験した422エラー1
  • は思ったより間違いの原因を見つけやすい.エラーの原因は、要求されたapiパラメータの検証問題(より正確には応答モデル)
  • である.
  • FastAPIは、APIパラメータのタイププロンプトを設定するだけでなく、タイプを検証します.
  • は本当にapiリクエストコントローラを簡単に作成しました.
    @app.router.get('/log/{age}', tags=['age'])
    async def get_log_by_age(self, age: int):
    	    response = await appHandler.get_log_by_age(age:int)
          return {'result': response, 'message': '', 'code': 200}
  • 例えば、
  • では、年齢別クエリーログのapiを記述すると、コントローラ側は入力パラメータを検証する.ageの場合、intタイプが受け入れられ、クライアントがfloatまたはstringタイプのデータを誤って入力した場合、422 Unprocessable Entityエラーが返される.
  • 私が経験した422エラー2
  • 明らかに要求パリミの検証も正確で異常はなかったが、再び422 Unprocessable Entityに遭遇した.
  • は最終的に検証問題を迂回したが、状況はやや異なる
  • であった.
    例えば、コントローラは、各種要求を処理するfunc()を有する、
    @app.router.get('/log/{age}', tags=['age'])
    async def get_log_by_age(self, age: int):
    	    response = await appHandler.get_log_by_age(age:int)
          return {'result': response, 'message': '', 'code': 200}
    
    @app.router.get('/log/region', tags=['region'])
    async def get_log_by_region(self, age: int):
    	    response = await appHandler.get_log_by_region(region: string = 'seoul')
          return {'result': response, 'message': '', 'code': 200}
    .
  • アルゴリズムは、パスパラメータが領域を含み、クエリパラメータが領域名(default='seoul')を含むapi要求ハンドルfunc()が同じコントローラスクリプト内にある場合、/log/region?region=seoulというapi要求が422 Unprocessable Entity(要求が正常に行われている場合)に戻ることを報告する.
  • 理由/log/region?region=seoulの/regionパスパラメータ部分は/log/{age}のage:intとみなされ、422 Unprocessable Entityに戻る.
  • 解決策
  • これらの問題はapi要求ハンドルfunc()の順序を変更することによって簡単に解決できる:
    @app.router.get('/log/region', tags=['region'])
    async def get_log_by_region(self, age: int):
    	    response = await appHandler.get_log_by_region(region: string = 'seoul')
          return {'result': response, 'message': '', 'code': 200}
    
    @app.router.get('/log/{age}', tags=['age'])
    async def get_log_by_age(self, age: int):
    	    response = await appHandler.get_log_by_age(age:int)
          return {'result': response, 'message': '', 'code': 200}
  • はこのようにHandler func()の順序を変更し、FastAPIは要求Handlerを検索するときに正常に処理プログラムを見つけることができる.