簡體   English   中英

Python:發送 JSON 數據時,FastAPI 錯誤 422 與 POST 請求

[英]Python: FastAPI error 422 with POST request when sending JSON data

我正在構建一個簡單的 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進行解析,並更改標頭,但對我沒有任何作用。

直接來自文檔

函數參數將被識別如下:

  • 如果參數也在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

對於 POST Requests 接收請求體,需要做如下操作

創建 Pydantic 基礎模型用戶

from pydantic import BaseModel

class User(BaseModel):
    user_name: str


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

(如果不是像上面那樣的語法錯誤)從發布請求接收響應 422 可能有很多原因。

可以通過以下方式復制此內容:

  • 編輯你的身體結構
  • 改變你的身體類型(發送任何字符串)
  • 更改/刪除您的 header 內容類型

我通常如何調試如下:

  1. 如果您使用的是 FastAPI,請使用內置的“/docs”路由在本地主機上進行測試,如果帖子在那里失敗,則可能是語法/邏輯錯誤,與您的帖子路由無關。 FastAPI 的這個特性非常有幫助。 注意 Post 請求不需要/期望 UI 上的標題,因為它為您提供了一個文本位置來填充它。

  2. 測試在可變主體端點上:您可以這樣設置:

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

使用任何 JSON 發送請求,如果仍然收到 422,則轉到下一步。

  1. 確保您的 header 內容類型正確,最常見的是:
 headers = {'Content-Type': 'application/json'};

這個錯誤似乎是在 FastAPI 完成之前 origin 掛斷的結果。

我從 Java 調用 FastAPI,它返回得太早了。

為了修復此錯誤,我添加了CompletableFuture<String>的用法並使用HTTPClient.sendAsync function,然后在 promise 上調用CompletableFuture.get

FastAPI 基於Python 類型提示,因此當您傳遞查詢參數時,它除了key : value對之外,您需要以某種方式聲明它。

即使這樣的事情也會起作用

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"}

就我而言,我正在像這樣從不同的 python 項目調用 python API

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

我正在使用數據屬性,我將其更改為 json,然后它對我有用

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

就我而言,我的 FastAPI 端點需要表單數據而不是 JSON。 因此,解決方法是發送表單數據而不是 JSON。 (注意:對於node-js,FormData不可用,可以使用form-data

此處發布的答案是正確的。 在發布請求時應該使用 Pydantic 模型(盡管關於表單數據,請參見此處)。 但是,如果不想使用 Pydantic 模型,他們也可以使用Body parameters 如果使用單個 body 參數(如您的示例中),您可以使用特殊的Body參數embed 下面是一個使用這個概念的工作示例。

應用程序.py

import uvicorn
from fastapi import Body, FastAPI

app = FastAPI()

@app.post("/")
async def main(user: str = Body(..., embed=True)):
    return {"user": user}
    
if __name__ == '__main__':
    uvicorn.run(app, host='127.0.0.1', port=8000, debug=True)

測試.py

import requests

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

如果您正在使用fetch API 並且仍然獲得422 Unprocessable Entity ,請確保您已設置Content-Type標頭:

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

這解決了我的問題。 在服務器端,我使用的是 Pydantic 模型,所以如果您不使用這些模型,請參閱上面的答案。

對我來說,問題是我的 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

這不是一個描述性很強的錯誤,而且很難找到原因。

我在使用 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 ,並在 function 簽名上將其聲明為 Body 參數,即

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

以上說法並不完全錯誤。 當我按原樣使用登錄端點時,它確實工作得很好。

使用 FastAPI 構建 - 授權按鈕

在嘗試使用授權按鈕進行身份驗證時,它使用login端點底層(因為這是作為 tokenUrl 傳遞的),這是我收到無法處理的實體錯誤的時候

希望這可以幫助

編輯 1(添加更多上下文)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM