FastAPIで、HTMLのform内容の送信ができないときの解決策
FastAPIに対してHTMLのform内容を送信したとき、422 Unprocessable Entity エラーが発生し、ブラウザには以下のような文章が表示されることがある。
{"detail":[{"loc":["query","変数名"],"msg":"field required","type":"value_error.missing"}]}
これが発生する例を紹介する。
- HTMLではtextの入力と、ファイルのアップロードができる
-
/create
に対してPOSTメソッドで入力内容を送信する
<div style="text-align: center">
<form action="/create" method="post" id="upload-form" enctype="multipart/form-data">
<label for="name-form">アプリケーションの名前</label> <br/>
<input name="app_name" type="text" id="name-form"><br/>
<br/>
<label for="file-form">Excelファイル</label> <br/>
<input name="file" type="file" id="file-form"><br/>
<br/>
<input type="submit" value="アップロード"/>
</form>
</div>
-
/create
ではapp_name
というstr型変数と、ファイルを受け取る
@app.post("/create")
async def create_upload_file(app_name: str, file: UploadFile = File(...)):
return {"filename": file.filename,
"app_name": app_name}
この構成のままsubmitすると
{"detail":[{"loc":["query","app_name"],"msg":"field required","type":"value_error.missing"}]}
という表示が出て、正常に処理が行われない。
その原因と、解決する方法を紹介する。
原因
関数の引数にパスパラメータにないものがあるとき、それは自動的にクエリパラメータと見なされるが、formがmethod="post"
のときはaction
のURLにクエリパラメータがつかないから。
上記の例では、引数にapp_name: str
があるものの、@app.post("/create/{ app_name }")
のようにパスパラメータとして指定されていないため、app_nameはクエリパラメータから取得される。
しかしaction="/create"
だと送信先のURLがhttp://127.0.0.1:8000/create
になり、クエリパラメータなしの状態でPOSTされるため、app_nameが取得できずエラーが発生する。
formのactionを書き換える
以上の問題をformのactionを書き換えることで解決する。
まずHTMLに以下のようなjavascriptを追加する。
function setAction(){
const path = "/create";
const key = "app_name";
const val = document.getElementById('name-form').value;
document.getElementById('upload-form').action = path+'?'+ key +'='+ val;
}
そしてsubmitボタンのonClick関数として設定する。
<input type="submit" value="アップロード" onclick="setAction()"/>
すると、formのactionの値がsubmitボタンを押した瞬間
action="/create?app_name=(フォームに入力した値)"
に書き換わり、クエリパラメータがついた状態でPOSTできるようになる。
これによって、422 Unprocessable Entity エラーが回避できる。
Discussion
Formクラスがありますよ。
ほんとだ!ありがとうございます