Python: FastAPI-Fehler 422 mit POST-Anfrage beim Senden von JSON-Daten

Lesezeit: 6 Minuten

Smiths Benutzeravatar
Schmied

Ich baue eine einfache API zum Testen einer Datenbank. Wenn ich es benutze GET Anfrage funktioniert alles gut, aber wenn ich auf wechsle POSTIch bekomme 422 Unprocessable Entity Fehler.

Hier ist der FastAPI-Code:

from fastapi import FastAPI

app = FastAPI()

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

Dann meine Anfrage mit JavaScript

let axios = require('axios')

data = { 
    user: 'smith' 
}

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

Auch mit Python requests:

import requests

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

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

Ich habe auch versucht, als JSON zu analysieren, was die Verwendung bedeutet utf-8und die Header ändern, aber bei mir hat nichts funktioniert.

  • Haben Sie den Server (wie uvicorn) gestartet, auf dem die Fastapi-Anwendung läuft?

    – Orkun Kocyigit

    27. Januar 2020 um 10:37 Uhr

Direkt aus der Dokumentation:

Die Funktionsparameter werden wie folgt erkannt:

  • Wenn der Parameter auch im deklariert ist Wegwird es als Pfadparameter verwendet.
  • Wenn der Parameter von a ist singulärer Typ (wie int, float, str, bool usw.) wird als a interpretiert Anfrage Parameter.
  • Wenn der Parameter als Typ a deklariert ist Pydantisches Modellwird es als Anfrage interpretiert Körper.”

Um also einen POST-Endpunkt zu erstellen, der einen Textkörper mit einem Benutzerfeld empfängt, würden Sie etwa Folgendes tun:

from fastapi import FastAPI
from pydantic import BaseModel


app = FastAPI()


class Data(BaseModel):
    user: str


@app.post("https://stackoverflow.com/")
def main(data: Data):
    return data

In meinem Fall habe ich die Python-API aus einem anderen Python-Projekt wie diesem aufgerufen

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

Ich habe die Dateneigenschaft verwendet, sie in JSON geändert, dann hat es bei mir funktioniert

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

  • Danke! Damit hat meine Funktion auch funktioniert … aber ich wünschte immer noch, ich wüsste, warum die Übergabe eines Diktats über Daten (wie in der Hilfedatei beschrieben) den Fehlerstatuscode 422 verursacht.

    – Mark Seagoe

    11. November 2021 um 0:59

  • Ich habe gelesen, dass das Datenfeld für das FormData-Format gedacht ist … das eine Javascript-Klasse zur Übergabe von HTML-Formulardaten zu sein scheint. github.com/tiangolo/fastapi/issues/3373

    – Mark Seagoe

    11. November 2021 um 3:03

  • Wow, die nicht verarbeitbare Entität 422 ist schließlich ein Datenformatierungsproblem. Der Fehlercode und die Fehlermeldung sind nicht explizit.

    – Alsushi

    16. März 2022 um 10:35 Uhr

Chris' Benutzer-Avatar
Chris

Eine Antwort mit Statuscode 422 (unprocessable entity) verfügt über einen Antworttext, der die Fehlermeldung angibt und genau angibt, welcher Teil Ihrer Anfrage fehlt oder nicht dem erwarteten Format entspricht. Der von Ihnen bereitgestellte Codeausschnitt zeigt, dass Sie etwas posten möchten JSON Daten an einen Endpunkt, der sie erwartet user sein query Parameter, statt JSON Nutzlast. Daher die 422 unprocessable entity Fehler. Unten sind angegeben vier verschiedene Optionen wie man einen zu erwartenden Endpunkt definiert JSON Daten.

Option 1

Gemäß der Dokumentationwenn Sie senden müssen JSON Wenn Sie Daten von einem Client (z. B. einem Browser) an Ihre API senden, senden Sie sie als Anfragetext (durch ein POST Anfrage). Um einen Anfragetext zu deklarieren, können Sie verwenden Pydantisch Modelle.

from pydantic import BaseModel

class User(BaseModel):
    user: str

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

Option 2

Wenn man keine Pydantic-Modelle verwenden möchte, könnte man sie auch verwenden Körper Parameter. Wenn ein einzelner Körperparameter verwendet wird (wie in Ihrem Beispiel), können Sie den speziellen verwenden Körper Parameter einbetten.

from fastapi import Body

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

Option 3

Eine andere (weniger empfohlene) Möglichkeit wäre die Verwendung von a Dict Typ (oder einfach dict in Python 3.9+), um a zu deklarieren key:value Paar. Auf diese Weise können Sie jedoch keine benutzerdefinierten Validierungen für verschiedene Attribute wie erwartet verwenden JSONwie Sie es mit Pydantic-Modellen tun würden oder Body Felder (z. B. prüfen, ob eine E-Mail-Adresse gültig ist oder ob eine Zeichenfolge einem bestimmten Muster folgt).

from typing import Dict, Any

@app.post("https://stackoverflow.com/")
def main(payload: Dict[Any, Any]):  # or, payload: dict[Any, Any]
    return payload

Option 4

Wenn Sie sicher sind, dass die eingehenden Daten gültig sind JSONkönnen Sie verwenden Starlettes Request direkt widersprechen um den Anfragetext als zu analysieren JSONverwenden await request.json(). Allerdings können Sie bei diesem Ansatz nicht nur keine benutzerdefinierten Validierungen für Ihre Attribute verwenden, sondern Sie müssten auch Ihren Endpunkt mit definieren async defseit request.json() ist ein async Methode und daher muss man await es (schauen Sie sich an diese Antwort Weitere Informationen zu def vs async def).

from fastapi import Request

@app.post("https://stackoverflow.com/")
async def main(request: Request): 
    return await request.json()

Wenn Sie möchten, können Sie auch einige Überprüfungen vornehmen Content-Type Fordern Sie den Header-Wert an, bevor Sie versuchen, die Daten zu analysieren, ähnlich wie bei dieser Antwort. Allerdings nur, weil eine Anfrage besagt application/json im Content-Type Header bedeutet dies nicht immer, dass dies wahr ist oder dass die eingehenden Daten gültig sind JSON (z. B. fehlt möglicherweise eine geschweifte Klammer, ein Schlüssel hat keinen Wert usw.). Daher könnten Sie a verwenden try-except blockieren, wenn Sie versuchen, die Daten zu analysieren, damit Sie alle Daten verarbeiten können JSONDecodeErrorfalls es ein Problem mit der Art und Weise gibt, in der Sie JSON Daten werden formatiert.

from fastapi import Request

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

Testen Sie die oben genannten Optionen

Verwendung der Python-Anforderungsbibliothek

Eine entsprechende Antwort finden Sie hier.

import requests

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

Verwendung von JavaScript API abrufen

Entsprechende Antworten finden Sie hier und auch hier. Für Beispiele mit axiosBitte schauen Sie sich diese Antwort sowie diese Antwort und diese Antwort an.

fetch("https://stackoverflow.com/", {
        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);
    });

  • Chris, dein Vorschlag zu „Payload“ hat mir geholfen. Vielen Dank

    – Iker

    26. April um 18:59 Uhr

Alans Benutzeravatar
Alan

Wenn Sie das verwenden fetch API und bekomme immer noch die 422 Nicht verarbeitbare Entitätstellen Sie sicher, dass Sie das eingestellt haben Inhaltstyp Header:

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

Dies hat das Problem in meinem Fall gelöst. Auf der Serverseite verwende ich Pydantic-Modelle. Wenn Sie diese also nicht verwenden, lesen Sie die obigen Antworten.

Yagiz Degirmencis Benutzeravatar
Yagiz Degirmenci

FastAPI basiert auf Hinweise zum Python-Typ Wenn Sie also einen Abfrageparameter übergeben, wird dieser akzeptiert Taste:Wert Paar müssen Sie es irgendwie deklarieren.

Sogar so etwas wird funktionieren

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

Out: {"user":"Smith"}

Aber die Verwendung von Pydantic ist viel effektiver

class User(BaseModel):
    user: str

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

Out: {"user":"Smith"}

Avinash Ravis Benutzeravatar
Avinash Ravi

Bei POST-Anfragen zur Aufnahme des Anfragetextes müssen Sie wie folgt vorgehen

Erstellen Sie einen Pydantic-Basismodellbenutzer

from pydantic import BaseModel

class User(BaseModel):
    user_name: str


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

Yongs Benutzeravatar
Yong

In meinem Fall erwartet mein FastAPI-Endpunkt Formulardaten anstelle von JSON. Daher besteht die Lösung darin, Formulardaten anstelle von JSON zu senden. (Hinweis: Für Node-JS ist FormData nicht verfügbar und Formulardaten kann verwendet werden)

1453330cookie-checkPython: FastAPI-Fehler 422 mit POST-Anfrage beim Senden von JSON-Daten

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy