Python:发送 JSON 数据时,FastAPI 出现 POST 请求错误 422

2024-11-25 08:49:00
admin
原创
605
摘要:问题描述:我正在构建一个简单的 API 来测试数据库。当我使用GET请求时,一切都正常,但如果我更改为POST,就会出现422 Unprocessable Entity错误。以下是 FastAPI 代码:from fastapi import FastAPI app = FastAPI() @app.po...

问题描述:

我正在构建一个简单的 API 来测试数据库。当我使用GET请求时,一切都正常,但如果我更改为POST,就会出现422 Unprocessable Entity错误。

以下是 FastAPI 代码:

from fastapi import FastAPI

app = FastAPI()

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

然后,我使用 JavaScript 的请求

let axios = require('axios')

data = { 
    user: 'smith' 
}

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

另外,使用 Python requests

import requests

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

response = requests.post(url, json=data)
print(response.text)

我也尝试将其解析为 JSON,使用 进行编码utf-8,并更改标题,但对我而言都不起作用。


解决方案 1:

具有状态代码的响应422(即Unprocessable entity)将具有指定错误消息的响应主体,准确说明请求的哪一部分缺失或与预期格式不匹配。您提供的代码片段显示您正尝试将数据发布到期望作为参数而不是有效负载的JSON端点。因此,出现错误。下面给出了四种不同的选项,介绍如何定义端点以期望数据,以及用于测试它们的 Python 和 JavaScript HTTP 客户端。user`queryJSON422 Unprocessable entity`JSON

选项 1

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

from fastapi import FastAPI
from pydantic import BaseModel


class Base(BaseModel):
    user: str


app = FastAPI()


@app.post('/')
async def main(base: Base):
    return base

选项 2

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

from fastapi import Body

@app.post('/')
async def main(user: str = Body(..., embed=True)):
    return {'user': user}

选项 3

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

from typing import Dict, Any

@app.post('/')
async def main(payload: Dict[Any, Any]): 
    return payload

在上面的例子中,payload也可以定义为payload: dict[Any, Any],或者简称payload: dict

选项 4

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

from fastapi import Request

@app.post('/')
async def main(request: Request): 
    return await request.json()

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

from fastapi import Request, HTTPException
from json import JSONDecodeError

@app.post('/')
async def main(request: Request):
    content_type = request.headers.get('Content-Type')
    
    if content_type is None:
        raise HTTPException(status_code=400, detail='No Content-Type provided')
    elif content_type == 'application/json':
        try:
            return await request.json()
        except JSONDecodeError:
            raise HTTPException(status_code=400, detail='Invalid JSON data')
    else:
        raise HTTPException(status_code=400, detail='Content-Type not supported')

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

测试上述所有选项

使用 Python 请求库

相关答案可以在这里找到。

import requests

url = 'http://127.0.0.1:8000/'
payload ={'user': 'foo'}
resp = requests.post(url=url, json=payload)
print(resp.json())

使用 JavaScript Fetch API

相关答案可在此处和此处找到。有关使用的示例axios,请查看此答案以及此答案和此答案。

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

解决方案 2:

直接来自文档:

函数参数将被识别如下:

  • 如果该参数也在路径中声明,它将被用作路径参数。

  • 如果参数是单一类型(如 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

解决方案 3:

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

queryResponse = requests.post(URL, data= query)

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

queryResponse = requests.post(URL, json = query)

解决方案 4:

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

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

这解决了我的问题。在服务器端,我使用的是 Pydantic 模型,因此如果您没有使用这些模型,请参阅上述答案。

解决方案 5:

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

即使像这样的事情也会起作用

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

Out: {"user":"Smith"}

但是使用 Pydantic 的方式更有效

class User(BaseModel):
    user: str

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

Out: {"user":"Smith"}

解决方案 6:

对于接收请求主体的 POST 请求,您需要执行以下操作

创建一个 Pydantic 基础模型用户

from pydantic import BaseModel

class User(BaseModel):
    user_name: str


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

解决方案 7:

就我的情况而言,我的 FastAPI 端点需要的是表单数据而不是 JSON。因此,解决方法是发送表单数据而不是 JSON。(注意:对于 node-js,FormData 不可用,可以使用表单数据)

解决方案 8:

(如果不是像上面那样的语法错误)从 post 请求收到响应 422 的原因可能有很多。

可以通过以下方式复制:

  • 编辑你的身体结构

  • 改变你的身体类型(发送任意字符串)

  • 更改/删除标题内容类型

我通常的调试方法如下:

  1. 如果您使用的是 FastAPI,请使用内置的“/docs”路由在本地主机上进行测试,如果帖子在那里失败,则可能是语法/逻辑错误,与您的帖子路由无关。FastAPI 的这个功能非常有用。注意 Post 请求不需要/不需要 UI 上的标头,因为它为您提供了一个文本位置来填写它。

  2. 测试是在变量主体端点上进行的:您可以像这样设置:

@app.post('/test')
async def function(objectName: dict = Body(...)):

发送带有任何 JSON 的请求,如果仍然收到 422,则转到下一步。

  1. 确保标题内容类型正确,最常见的是:

headers = {'Content-Type': 'application/json'};

解决方案 9:

该错误似乎是由于在 FastAPI 完成之前源就挂断了而导致的。

我从 Java 调用 FastAPI 但返回得太早了。

为了修复这个错误,我添加了用法CompletableFuture<String>和使用HTTPClient.sendAsync函数,然后调用CompletableFuture.get了承诺。

解决方案 10:

对我来说,问题是我的 POST 主体不包含端点正在寻找的所有属性:

邮政

{
    "gateway": "",
    "nameservers": [],
    "deleteWiFi": true,
    "ssidPassword": ""
}

FastAPI Python

class SubmitWiFi(BaseModel):
    gateway: str
    addresses: list[str]    # missing
    nameservers: list[str]
    deleteWiFi: bool
    ssid: str   # missing
    ssidPassword: str

@app.post("/submitWiFi")
async def submitWiFi(data: SubmitWiFi):
    # my code

这不是一个描述性很强的错误,很难找到原因。

解决方案 11:

我在使用 FastAPI 进行用户身份验证时遇到了“POST /login HTTP/1.1”422 Unprocessable Entity错误。这个问题是由于我从客户端捕获身份验证数据的方式造成的。我将分享解决方案以及我做错的事情

解决方案
from fastapi.security import OAuth2PasswordBearer
from fastapi.security import OAuth2PasswordRequestForm
from fastapi import Depends

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

@app.post('/login', response_model=Dict)
def login(
        payload: OAuth2PasswordRequestForm = Depends(),   # <--- here's all that matters
        session: Session = Depends(get_db)
    ):
    ### login logic

在我的例子中,我没有使用OAuth2PasswordRequestForm,而是使用了 UserScheme 来捕获usernamepassword在函数签名中将其声明为 Body 参数,即

@app.post('/login', response_model=Dict)
def login(
        payload: UserSchema = Body()
       ....
       ....
)

以上内容并非完全错误。当我按原样使用登录端点时,它确实运行良好。

使用 FastAPI 构建 - 授权按钮

当我尝试使用授权按钮进行身份验证时,该按钮使用login终端底层(因为这是作为 tokenUrl 传递的),这时我得到了无法处理的实体错误

希望这有帮助

编辑 1(添加更多上下文)

解决方案 12:

简短的回答:该错误表明传递了错误的内容类型。

当我将 FastAPI 从0.61.2升级到0.101.1时,我开始从 curl 收到这些错误。我的命令如下:

curl -i -X POST http://localhost:8000/model -d '{"text":"o"}'

事实证明,新的 FastAPI 需要指定内容类型:

curl -i -X POST http://localhost:8000/model -H "Content-type: application/json" -d '{"text":"o"}'

解决方案 13:

在你的 FastAPI 代码中,你只需要定义你想要接受为请求主体的用户变量。为此,你只需要在你的 FastAPI 代码中做一些小的更改:

from fastapi import Body, FastAPI

app = FastAPI()

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

并且你的 javascript 代码不需要任何更改:

let axios = require('axios')

data = { 
    user: 'smith' 
}

axios.post('http://localhost:8000', data)
    .then(response => (console.log(response)))
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用