Python:带有 post 请求的 FastAPI 错误 422

Smi*_*ith 15 python python-requests axios fastapi

我正在构建一个简单的 API 来测试数据库。当我使用 get request 时一切正常,但如果我更改为 post,我会收到“无法处理的实体”错误:

这是 FastAPI 代码:

from fastapi import FastAPI

app = FastAPI()

@app.post("/")
def main(user):
    return user
Run Code Online (Sandbox Code Playgroud)

然后,我的请求使用 javascript

let axios = require('axios')

data = { 
    user: 'smith' 
}

axios.post('http://localhost:8000', data)
    .then(response => (console.log(response.url)))
Run Code Online (Sandbox Code Playgroud)

并使用 Python

import requests

url = 'http://127.0.0.1:8000'
data = {'user': 'Smith'}

response = requests.post(url, json=data)
print(response.text)
Run Code Online (Sandbox Code Playgroud)

我也尝试解析为 json,使用 utf-8 编码,并更改标题。没有什么对我有用。

Chr*_*ris 62

422具有( ) 状态代码的响应unprocessable entity将具有指定错误消息的响应正文,准确说明请求的哪一部分丢失或与\xe2\x80\x99 不匹配预期格式。您提供的代码片段显示您正在尝试将JSON数据发布到期望user作为query参数而不是JSON有效负载的端点。因此,出现422 unprocessable entity错误。下面给出了关于如何定义期望数据的端点的四种不同选项JSON

\n

选项1

\n

根据文档,当您需要将JSON数据从客户端(比如说浏览器)发送到您的 API 时,您可以将其作为请求正文发送(通过POST请求)。要声明请求正文,您可以使用Pydantic模型。

\n
from pydantic import BaseModel\n\nclass User(BaseModel):\n    user: str\n\n@app.post(\'/\')\ndef main(user: User):\n    return user\n
Run Code Online (Sandbox Code Playgroud)\n

选项2

\n

如果不想使用 Pydantic 模型,也可以使用Body参数。如果使用单个 body 参数(如您的示例中所示),您可以使用特殊的Body参数embed

\n
from fastapi import Body\n\n@app.post(\'/\')\ndef main(user: str = Body(..., embed=True)):\n    return {\'user\': user}\n
Run Code Online (Sandbox Code Playgroud)\n

选项3

\n

另一种(不太推荐)方法是使用Dict类型(或简单地dict在 Python 3.9+ 中)来声明一个key:value对。但是,通过这种方式,您不能JSON像使用 Pydantic 模型或那样对您预期的各种属性使用自定义验证Body字段那样对您预期的各种属性使用自定义验证(例如,检查电子邮件地址是否有效,或者字符串是否遵循特定模式) 。

\n
from typing import Dict, Any\n\n@app.post(\'/\')\ndef main(payload: Dict[Any, Any]): \n    return payload\n
Run Code Online (Sandbox Code Playgroud)\n

在上面的示例中,payload也可以定义为payload: dict[Any, Any],或简单地定义为payload: dict

\n

选项4

\n

如果您确信传入的数据是有效的JSON,则可以直接使用Starlette 的Request对象来将请求正文解析为JSON,使用await request.json()。但是,使用这种方法,您不仅不能对属性使用自定义验证,而且还需要使用 定义端点async def,因为request.json()是一种async方法,因此需要await它(看看这个答案有关defvs的更多详细信息async def)。

\n
from fastapi import Request\n\n@app.post(\'/\')\nasync def main(request: Request): \n    return await request.json()\n
Run Code Online (Sandbox Code Playgroud)\n

如果您愿意,您还可以Content-Type在尝试解析数据之前对请求标头值进行一些检查,类似于此答案。然而,仅仅因为请求application/jsonContent-Type标头中说明,并不总是意味着这是真的,或者传入的数据是有效的JSON(即,可能缺少大括号,有一个没有值的键) , ETC)。因此,try-except当您尝试解析数据时,您可以使用块,让您处理任何数据JSONDecodeError,以防您的数据处理方式出现问题。JSON,以防数据格式化

\n
from fastapi import Request, HTTPException\nfrom json import JSONDecodeError\n\n@app.post(\'/\')\nasync def main(request: Request):\n    content_type = request.headers.get(\'Content-Type\')\n    \n    if content_type is None:\n        raise HTTPException(status_code=400, detail=\'No Content-Type provided\')\n    elif content_type == \'application/json\':\n        try:\n            return await request.json()\n        except JSONDecodeError:\n            raise HTTPException(status_code=400, detail=\'Invalid JSON data\')\n    else:\n        raise HTTPException(status_code=400, detail=\'Content-Type not supported\')\n
Run Code Online (Sandbox Code Playgroud)\n

如果您希望端点接受特定/预定义和任意 JSON 数据,请检查此答案

\n

测试以上选项

\n

使用Python请求库

\n

相关答案可以在这里找到

\n
import requests\n\nurl = \'http://127.0.0.1:8000/\'\npayload ={\'user\': \'foo\'}\nresp = requests.post(url=url, json=payload)\nprint(resp.json())\n
Run Code Online (Sandbox Code Playgroud)\n

使用 JavaScript获取 API

\n

相关答案也可以在这里这里找到。有关使用的示例axios,请查看这个答案,以及这个答案这个答案

\n
fetch(\'/\', {\n        method: \'POST\',\n        headers: {\n            \'Content-Type\': \'application/json\'\n        },\n        body: JSON.stringify({\'user\': \'foo\'})\n    })\n    .then(resp => resp.json()) // or, resp.text(), etc\n    .then(data => {\n        console.log(data); // handle response data\n    })\n    .catch(error => {\n        console.error(error);\n    });\n
Run Code Online (Sandbox Code Playgroud)\n


Sun*_*arg 49

就我而言,我从不同的 python 项目调用 python API,如下所示

queryResponse = requests.post(URL, data= query)
Run Code Online (Sandbox Code Playgroud)

我正在使用 data 属性,我将其更改为 json,然后它对我有用

queryResponse = requests.post(URL, json = query)
Run Code Online (Sandbox Code Playgroud)


Ala*_*lan 17

如果您正在使用fetchAPI 并且仍然收到422 Unprocessable Entity,请确保您已设置Content-Type标头:

fetch(someURL, {
  method: "POST",
  headers: {
    "Content-type": "application/json"
  },
  body
}).then(...)
Run Code Online (Sandbox Code Playgroud)

这解决了我的案例中的问题。在服务器端,我使用 Pydantic 模型,所以如果您不使用这些模型,请参阅上面的答案。


小智 15

直接来自文档

函数参数将被识别如下:

  • 如果参数也在path 中声明,它将用作路径参数。
  • 如果参数是单一类型(如 int、float、str、bool 等),它将被解释为查询参数。
  • 如果参数被声明为Pydantic 模型的类型,它将被解释为请求正文。”

因此,要创建一个接收带有用户字段的正文的 POST 端点,您可以执行以下操作:

from fastapi import FastAPI
from pydantic import BaseModel


app = FastAPI()


class Data(BaseModel):
    user: str


@app.post("/")
def main(data: Data):
    return data
Run Code Online (Sandbox Code Playgroud)


Yag*_*nci 5

FastAPI 基于Python 类型提示,因此当您传递查询参数时,它接受key : value对,您需要以某种方式声明它。

即使像这样的东西也会起作用

from typing import Dict, Any
...
@app.post("/")
def main(user: Dict[Any, Any] = None):
    return user

Out: {"user":"Smith"}
Run Code Online (Sandbox Code Playgroud)

但使用Pydantic方式更有效

class User(BaseModel):
    user: str

@app.post("/")
def main(user: User):
    return user

Out: {"user":"Smith"}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

28947 次

最近记录:

4 年,4 月 前