Search code examples
pythonjsonpython-requestsfastapipydantic

How to post JSON data that include unicode characters to FastAPI using Python requests?


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.


Solution

  • 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)