问题描述:
When a FastAPI endpoint expects a Pydantic model and one is passed with a string it works as expected unless that string contains unicode characters.
First I create an example application for FastAPI with an example model.serv.py
from pydantic import BaseModel
class exClass(BaseModel):
id: int = Field(example=1)
text: str = Field(example="Text example")
app = FastAPI(debug=True)
@app.post("/example")
async def receive_pyd(ex: exClass):
print(ex)
return True
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
The client that shows the error in question client.py
from pydantic import BaseModel, Field
import requests
class exClass(BaseModel):
id: int = Field(example=1)
text: str = Field(example="Text example")
ex1 = exClass(id=1, text="working example")
ex2 = exClass(id=2, text="this’ will fail")
ex3 = exClass(id=3, text="🤗 <- also non-working")
r = requests.post(f"http://127.0.0.1:8000/example", data=ex1.model_dump_json())
print(r.text)
r = requests.post(f"http://127.0.0.1:8000/example", data=ex2.model_dump_json())
print(r.text)
r = requests.post(f"http://127.0.0.1:8000/example", data=ex3.model_dump_json())
print(r.text)
Output:
true
Invalid HTTP request received.
Invalid HTTP request received.
When text
contains unicode characters the result is a 422 Unprocessable Entity. I have tried ex.dict(), model_dump(), and using json instead of data in the requests call. Enabling debugging in FastAPI/starlette bubbles up that the Invalid HTTP request is a JSON decode error.
解决方案 1[最佳方案][1]
This is not a problem of Pydantic and FastAPI. You should encode you request data like it’s shown below:
r = requests.post(
f"http://127.0.0.1:8000/example", data=ex1.model_dump_json().encode('utf-8')
)
print(r.text)
r = requests.post(
f"http://127.0.0.1:8000/example", data=ex2.model_dump_json().encode('utf-8')
)
print(r.text)
r = requests.post(
f"http://127.0.0.1:8000/example", data=ex3.model_dump_json().encode('utf-8')
)
print(r.text)
解决方案 2:[2]
When sending JSON data from Python requests
, one should use the json
argument to pass a valid dictionary. Using that argument would set the request’s Content-Type
header to application/json
. The data
argument, on the other hand, is used when sending form data, and these data are encoded with application/x-www-form-urlencoded
(that is the default Content-Type
in requests
), or multipart/form-data
(if files
are included in the request as well). Please have a look at this answer and this answer. You might find this, as well as this and this helpful as well.
Along with setting the Content-Type
header to application/json
, you should use Pydantic’s model_dump()
method (see this answer for more details)—instead of model_dump_json()
—which would convert the model to a dictionary.
Example
# ... rest of the code is the same as given in the question
url = 'http://127.0.0.1:8000/example'
r = requests.post(url, json=ex1.model_dump())
print(r.text)
r = requests.post(url, json=ex2.model_dump())
print(r.text)
r = requests.post(url, json=ex3.model_dump())
print(r.text)
参考链接:
Copyright Notice: This article follows StackOverflow’s copyright notice requirements and is licensed under CC BY-SA 3.0.
Article Source: StackOverflow
[1] Yurii Motov
[2] Chris