gpt4 book ai didi

python - 使用 FastAPI 使用属性和 key 发出 POST 请求时出现 "422 Unprocessable Entity"错误

转载 作者:行者123 更新时间:2023-12-05 05:51:17 47 4
gpt4 key购买 nike

我有一个名为 main.py 的文件,如下所示:

from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

fake_db = {
"Foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"Bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}

class Item(BaseModel):
id: str
title: str
description: Optional[str] = None

@app.post("/items/", response_model=Item)
async def create_item(item: Item, key: str):
fake_db[key] = item
return item

现在,如果我运行保存在文件 test_main.py 中的测试代码

from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_create_item():
response = client.post(
"/items/",
{"id": "baz", "title": "A test title", "description": "A test description"},
"Baz"
)
return response.json()

print(test_create_item())

没有得到想要的结果,就是

{"id": "baz", "title": "A test title", "description": "A test description"}

错在哪里?

最佳答案

让我们先解释一下您做错了什么。


FastAPI's TestClient 只是 Starlette's TestClient 的再导出这是 requests.Session 的子类.请求库的 post 方法具有以下签名:

def post(self, url, data=None, json=None, **kwargs):
r"""Sends a POST request. Returns :class:`Response` object.

:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) json to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:rtype: requests.Response
"""

你的代码

response = client.post(
"/items/",
{"id": "baz", "title": "A test title", "description": "A test description"},
"Baz",
)

做错了很多事:

  1. 它正在将参数传递给两个 data json参数,这是错误的,因为你不能有 2 个不同的请求主体。你要么传入 data json , 但不是两者data通常用于来自 HTML 表单的表单编码输入,而 json适用于原始 JSON 对象。查看requests docs on "More complicated POST requests" .
  2. 请求库将简单地删除 json争论是因为:

    Note, the json parameter is ignored if either data or files is passed.

  3. 传入的是纯字符串"Baz"json参数,它不是有效的 JSON 对象。
  4. data参数需要表单编码数据。

FastAPI 在响应中返回的完整错误是:

def test_create_item():
response = client.post(
"/items/", {"id": "baz", "title": "A test title", "description": "A test description"}, "Baz"
)
print(response.status_code)
print(response.reason)
return response.json()

# 422 Unprocessable Entity
# {'detail': [{'loc': ['query', 'key'],
# 'msg': 'field required',
# 'type': 'value_error.missing'},
# {'loc': ['body'],
# 'msg': 'value is not a valid dict',
# 'type': 'type_error.dict'}]}

第一个错误是 key查询中缺少,这意味着路由参数 key"Baz"不在请求正文中,FastAPI 尝试从查询参数中查找它(请参阅 Request body + path + query parameters 上的 FastAPI 文档)。

第二个错误来自上面列出的关于 data 的第 4 点格式编码不正确(当您将 dict 值包装在 json.dumps 中时,该错误确实消失了,但这并不重要,也不是解决方案的一部分)。

你说 in a comment你试图做与 FastAPI Testing Tutorial 中相同的事情.该教程与您的代码的不同之处在于它发布了 Item 的所有属性。对象在 1 个主体中,并且它正在使用 json= .post 的参数.


现在是解决方案!

解决方案 #1:有一个单独的类,用于使用键 POST 项目属性

在这里,您需要 2 个类,一个带有 key您用于 POST 请求正文的属性(我们称之为 NewItem ),以及您当前的属性 Item对于内部数据库和响应模型。你的路由函数将只有 1 个参数( new_item ),你可以只得到 key来自那个对象。

主.py

class Item(BaseModel):
id: str
title: str
description: Optional[str] = None

class NewItem(Item):
key: str

@app.post("/items/", response_model=Item)
async def create_item(new_item: NewItem):
# See Pydantic https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict
# Also, Pydantic by default will ignore the extra attribute `key` when creating `Item`
item = Item(**new_item.dict())
print(item)
fake_db[new_item.key] = item
return item

测试用.post代码,使用 json=传递 1 个字典中的所有字段。

test_main.py

def test_create_item():
response = client.post(
"/items/",
json={
"key": "Baz",
"id": "baz",
"title": "A test title",
"description": "A test description",
},
)
print(response.status_code, response.reason)
return response.json()

输出

id='baz' title='A test title' description='A test description'
200 OK
{'description': 'A test description', 'id': 'baz', 'title': 'A test title'}

解决方案 #2:有 2 个正文部分,1 个用于项目属性,1 个用于键

您可以改为像这样构建 POSTed 正文:

{
"item": {
"id": "baz",
"title": "A test title",
"description": "A test description",
},
"key": "Baz",
},

你在哪里有 Item嵌套字典中的属性,然后有一个简单的 key -与item处于同一级别的值对. FastAPI 可以处理这个,请参阅 Singular values in body 上的文档,这非常适合您的示例:

For example, extending the previous model, you could decide that you want to have another key importance in the same body, besides the item and user.

If you declare it as is, because it is a singular value, FastAPI will assume that it is a query parameter.

But you can instruct FastAPI to treat it as another body key using Body

注意我强调的部分,关于告诉 FastAPI 查找 key在同一个 body 里。这里重要的是参数名称 itemkey匹配请求正文中的内容。

主.py

from fastapi import Body, FastAPI

class Item(BaseModel):
id: str
title: str
description: Optional[str] = None

@app.post("/items/", response_model=Item)
async def create_item(item: Item, key: str = Body(...)):
print(item)
fake_db[key] = item
return item

同样,为了制作 .post请求,使用 json=传递整个字典。

test_main.py

def test_create_item():
response = client.post(
"/items/",
json={
"item": {
"id": "baz",
"title": "A test title",
"description": "A test description",
},
"key": "Baz",
},
)
print(response.status_code, response.reason)
return response.json()

输出

id='baz' title='A test title' description='A test description'
200 OK
{'description': 'A test description', 'id': 'baz', 'title': 'A test title'}

关于python - 使用 FastAPI 使用属性和 key 发出 POST 请求时出现 "422 Unprocessable Entity"错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70404952/

47 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com