📕
UnityでPythonサーバ(FastAPI)にJSON形式でPOST送信時の422エラー解決方法
前書き
Unity初心者の私が非常に悩まされたことをまとめて、今後の迷える子羊への参考になればいいと思い書きました。
至らぬ点やもっといい解決策があれば教えていただきたいです。
環境
- Unity:2022.3.8f1
- Python:3.10.13
- FastAPI:0.103.0
解決できること
unity(c#)からJSON形式でPython(FastAPI)に送信
Python エラーと解決
FastAPIでPOSTリクエストで受け取るときには
エラー内容
POST送信された際422
エラーが出力された
422
エラーが出た時に使っていたコード
api.py
from fastapi import FastAPI
import subprocess
import os
@app.post("/text")
def testPost(text:str):
return {"text":text}
if __name__ == "__main__":
name, ex = os.path.splitext(os.path.basename(__file__))
subprocess.run(f"uvicorn {name}:app --reload")
解決
- リクエストボディの定義
- 422エラーのデバック
全体のコード
api.py
from pydantic import BaseModel
from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
import subprocess
import os
@app.exception_handler(RequestValidationError)
async def handler(request:Request, exc:RequestValidationError):
print(exc)
return JSONResponse(content={}, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)
class Item(BaseModel):
text:str
@app.post("/text")
def testPost(data:Item):
return {"text":data.text}
if __name__ == "__main__":
name, ex = os.path.splitext(os.path.basename(__file__))
subprocess.run(f"uvicorn {name}:app --reload")
1.リクエストボディの定義
POSTリクエストはデータを送信するためにリクエストボディ使用します。
リクエストボディを定義するにはpydantic
ライブラリを使います。
pydanticからBaseModelをインポートして継承したクラスを作成します。
pip
でpydantic
ライブラリのインストール
pip install pydantic
api.py
from pydantic import BaseModel
from fastapi import FastAPI
import subprocess
import os
# リクエストボディ
class Item(BaseModel):
text:str
@app.post("/text")
def testPost(data:Item):
return {"text":data.text}
if __name__ == "__main__":
name, ex = os.path.splitext(os.path.basename(__file__))
subprocess.run(f"uvicorn {name}:app --reload")
Item
クラスにBaseModel
を継承して使います。
2.422エラーのデバック
以下の記事を参考にコードを追記する
from pydantic import BaseModel
from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def handler(request:Request, exc:RequestValidationError):
print(exc)
return JSONResponse(content={}, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)
# 以下コード
追記して422エラーになると以下ものが出ます.
msgには「値は有効な辞書ではない」と出ている
422エラー
[{'loc': ('body',), 'msg': 'value is not a valid dict', 'type': 'type_error.dict'}]
Unity エラーと解決
エラー内容
- APIに送信するデータが辞書型ではない
- 辞書型で送信するのに
form.AddField
を使うのを間違っていた
TestPost.cs
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class TestPost : MonoBehaviour
{
private string serverURL = "http://127.0.0.1:8000/"; // サーバーのURLに置き換える
private void Start()
{
StartCoroutine("Test2voice");
}
IEnumerator Test2voice()
{
WWWForm form = new WWWForm();
// ▼ここから▼
form.AddField("text", "こんにちは、これはテストです");
UnityWebRequest request = UnityWebRequest.Post(serverURL + "text", form);
yield return request.SendWebRequest();
// ▲ここまでが原因▲
if (request.result == UnityWebRequest.Result.Success)
{
Debug.Log("フォームデータが正常に送信されました。");
Debug.Log(request.downloadHandler.text);
// 必要に応じてレスポンスを処理します。
}
else
{
Debug.LogError("フォームデータの送信中にエラーが発生しました: " + request.error);
}
}
}
解決
- JSONデータのクラスを作成
-
JsonUtility.ToJson
を使いstring型のJSON形式で返す - ASCIIコードで送信しないようにする
TestPost.cs
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class TestPost : MonoBehaviour
{
private string serverURL = "http://127.0.0.1:8000/"; // サーバーのURLに置き換える
// JSONデータのクラス
[System.Serializable]
private class RequestData
{
public string text;
}
private void Start()
{
StartCoroutine("Test2voice");
}
IEnumerator Test2voice()
{
RequestData requestData = new RequestData
{
text = "こんにちは、これはテストです",
};
// JSONデータを文字列に変換
string json = JsonUtility.ToJson(requestData);
// Debug.Log(json);
WWWForm form = new WWWForm();
// string型をbyte型配列に変換して送信
byte[] postData = System.Text.Encoding.UTF8.GetBytes(json);
var request = new UnityWebRequest(serverURL + "text", "POST");
request.uploadHandler = (UploadHandler)new UploadHandlerRaw(postData);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
yield return request.Send();
if (request.result == UnityWebRequest.Result.Success)
{
Debug.Log("フォームデータが正常に送信されました。");
Debug.Log(request.downloadHandler.text);
// 必要に応じてレスポンスを処理します。
}
else
{
Debug.LogError("フォームデータの送信中にエラーが発生しました: " + request.error);
}
}
}
new UnityWebRequest(serverURL + "text", "POST");
上記のコードのserverURL + "text"
はFastAPIのhttp://127.0.0.1:8000/text
に送信する
最後に
こういったエラーはbing aiやChatGPTに聞くのも、解決の糸口になるのでガンガン使っていきましょう。
Discussion