{'detail': [{'loc': ['body'], 'msg': 'value is not a valid dict', 'type': 'type_error.dict'}]} が取れない!
結論から先に言うと
json_payload ='{"ID":"M005","Name":"Aya","Class":"C"}'
の 'と"が付いているかどうか、だったというオチでした。
のように、pythonでFastAPIを使って、外部と内部のインターフェースのテンプレートを作るというのが、目下の課題でした。
多くのサンプルプログラムがあったのですが、私はhttpのGET/POSTができれば十分だったので、他のことは忘れて、そのポイントのみに絞って、ネットを探し回りました。
まずは、FastAPIを受ける側(サーバと言えるのかな)のプログラムは、
Python | FastAPIでAPI作成 ~その6:DELETEでデータ削除 & Pandas活用
を参考(というかコピペ)させて頂きました。
以下のコードを、私の場合はC:\Users\ebata\fastapi4に、index.pyという名前で置きました。
# https://miseruit.com/2022/07/18/post-2939/
from fastapi import FastAPI
from pydantic import BaseModel
import pandas
class Item(BaseModel):
ID: str
Name: str
Class: str
app = FastAPI() #インスタンスを作成
User_list =[
{"ID":"M001","Name":"Takashi","Class":"A"},
{"ID":"M002","Name":"Hanako","Class":"A"},
{"ID":"M003","Name":"Hiroshi","Class":"B"},
{"ID":"M004","Name":"Kyoko","Class":"B"},
]
# joson_normalize関数を用いてJsonデータをDataFrame型に変換します。
df = pandas.json_normalize(User_list)
# Get /Users/ : 全件取得
# Get /Users/{ID} :特定のIDのみ取得
# POST /Users/ :ユーザの登録
# DELETE /Users/{ID} :ユーザの削除
# curl http://localhost:8000/Users/
@app.get("/Users/")
async def users():
return User_list
# curl http://localhost:8000/Users/M003
@app.get("/Users/{u_id}")
async def users(u_id:str):
return list(filter(lambda item : item['ID']==u_id, User_list))
# Windowsの場合
# {"ID":"M005","Name":"Aya","Class":"C"}]を追加する
# curl -X POST -H "accept: application/json" -H "Content-Type: application/json" -d "{\"ID\":\"M005\", \"Name\":\"Aya\", \"Class\":\"C\"}" http://localhost:8000/Users/
@app.post("/Users/")
async def users(user: Item):
User_list.append({"ID": user.ID,"Name":user.Name,"Class":user.Class})
return User_list
# curl -X DELETE -H "accept: application/json" http://localhost:8000/Users/M003
@app.delete("/Users/{u_id}")
async def users(u_id:str):
global df
df = df.drop(df.index[df["ID"]==u_id])
return df.to_json(orient='records')
このプログラムは、メモリ上の名簿を検索(GET)、追加(POST)、削除(DELETE)するものです。
C:\Users\ebata\fastapi4> uvicorn index:app --reload
で起動します。
多分、プログラムの方から、「pipでxxxxを入れろ」と文句を言ってきますので、大人しく従います。
プログラム中に記載されている、curlのコマンドを使うとFastAPIの動作を確認できます。
さて、このプログラムで基本動作は確認できますが、基本的にクライアントのプログラムの叩き台がなければ、使えません。モジュールプログラムの中から、curlのコマンドを発行しろというのも乱暴な話です。
ですので、サクッとクライアント(というかFastAPIのアクセス用のテストプログラム)を作成してしまうと思ったのですが、ここで嵌りました。
先ずは、以下のコードをrequest.pyという名前で、C:\Users\ebata\fastapi4に置きました。
(以下のファイルは、動きたてのコードを修正せずに、汚いままで展開しています)
import requests
import json
# テスト
#url = 'https://wp.kobore.net/'
#response = requests.get(url)
#print(response.text)
#print()
print("-----start of get(1)")
# curl http://localhost:8000/Users/ と同じ機能を実施
r = requests.get('http://localhost:8000/Users/')
print(r.url)
print(r.status_code)
print(r.text)
print(r.json())
print("-----end of get(1)")
print()
# curl http://localhost:8000/Users/M003 と同じ機能を実施
print("-----start of get(2)")
r = requests.get('http://localhost:8000/Users/M003')
print(r.url)
print(r.status_code)
print(r.text)
print(r.json())
print("-----end of get(2)")
print()
print("-----start of delete")
# curl -X DELETE -H "accept: application/json" http://localhost:8000/Users/M003と同じ機能を実施
r = requests.delete('http://localhost:8000/Users/M003')
print(r.url)
print(r.status_code)
print(r.text)
print(r.json())
print("-----end of delete")
print()
# curl -X POST -H "accept: application/json" -H "Content-Type: application/json" -d "{\"ID\":\"M005\", \"Name\":\"Aya\", \"Class\":\"C\"}" http://localhost:8000/Users/ と同じ機能を実現
# {"ID":"M005","Name":"Aya","Class":"C"}]を追加する
headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
json_payload = '{"ID":"M005","Name":"Aya","Class":"C"}' # ここで100回くらいのパターンを試したぞ
print("headers:",headers)
print("payload:",json_payload)
#r = requests.post('http://localhost:8000/Users/',headers=headers, params=payload)
#r = requests.post('http://localhost:8000/Users/', headers=headers,params=json.loads(payload))
r = requests.post('http://localhost:8000/Users/', headers=headers, data=json_payload)
print(r.url)
print(r.status_code)
print(r.text)
print(r.json())
で、
C:\Users\ebata\fastapi4> python request.py
にて、GETとDELETEは問題なくサクっと動いたのですが、POSTだけが思うように動かない
{'detail': [{'loc': ['body'], 'msg': 'value is not a valid dict', 'type': 'type_error.dict'}]}{"detail":[{"loc":["body"],"msg":"field required","type":"value_error.missing"}]}エラー番号422
やらが、ワラワラと出てきて、頭を抱えました。
色々検索して調べると『こうやればいい』というアドバイスだけして、サンプルコード(1行で足る)を残さない奴が、世界中には山ほどいます(アドバイスではなく、コードを残して欲しい)。
のコードを真似て、ようやく動かすことができました(本当に助かりました)。
とりあえず、これでサーバとクライアントの対(つい)が完成し、実プログラムに展開できる準備ができたと思います。
以上