I'm building a simple API to test a database. When I use get request everything works fine, but if I change to post I get "unprocessable entity" error:
Here is the FastAPI code:
from fastapi import FastAPI
app = FastAPI()
@app.post("/")
def main(user):
return user
Then, my request using javascript
let axios = require('axios')
data = {
user: 'smith'
}
axios.post('http://localhost:8000', data)
.then(response => (console.log(response.url)))
Also using Python
import requests
url = 'http://127.0.0.1:8000'
data = {'user': 'Smith'}
response = requests.post(url, json=data)
print(response.text)
I also try to parse as json, enconding using utf-8, and change the headers. Nothing has worked for me.
Broadly speaking, if you see an HTTP 422 error it means the server understands your request, but it can't fulfill it due to a problem on your end. If you fix that problem, you should be able to reload the page and the error will go away.
The HyperText Transfer Protocol (HTTP) 422 Unprocessable Entity response status code indicates that the server understands the content type of the request entity, and the syntax of the request entity is correct, but it was unable to process the contained instructions.
In an ideal world, 422 is preferred and generally acceptable to send as response if the server understands the content type of the request entity and the syntax of the request entity is correct but was unable to process the data because its semantically erroneous.
Straight from the documentation:
The function parameters will be recognized as follows:
- If the parameter is also declared in the path, it will be used as a path parameter.
- If the parameter is of a singular type (like int, float, str, bool, etc) it will be interpreted as a query parameter.
- If the parameter is declared to be of the type of a Pydantic model, it will be interpreted as a request body."
So to create a POST endpoint that receives a body with a user field you would do something like:
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Data(BaseModel): user: str @app.post("/") def main(data: Data): return data
In my case, I was calling the python API from different python project like this
queryResponse = requests.post(URL, data= query)
I was using the data property, I changed it to json, then it worked for me
queryResponse = requests.post(URL, json = query)
If you're using the fetch
API and still getting the 422 Unprocessable Entity, ensure that you have set the Content-Type header:
fetch(someURL, {
method: "POST",
headers: {
"Content-type": "application/json"
},
body
}).then(...)
This solved the issue in my case. On the server-side I'm using Pydantic models, so if you aren't using those, see the above answers.
A response having status code 422
(unprocessable entity
) will have a response body that specifies the error message, telling exactly which part of your request is missing or doesn’t match the expected format. The code snippet you povided shows that you are trying to post JSON
data to an endpoint that is expecting user
being a query
parameter, rather than JSON
payload. Hence, the 422 unprocessable entity
error. Below are given four different options on how to define an endpoint to expect JSON
data.
As per the documentation, when you need to send JSON
data from a client (let's say, a browser) to your API, you send it as a request body (through a POST
request). To declare a request body, you can use Pydantic models.
from pydantic import BaseModel
class User(BaseModel):
user: str
@app.post("/")
def main(user: User):
return user
If one doesn't want to use Pydantic models, they could also use Body parameters. If a single body parameter is used (as in your example), you can use the special Body parameter embed.
from fastapi import Body
@app.post("/")
def main(user: str = Body(..., embed=True)):
return {"user": user}
Another (less recommended) way would be to use a Dict
type (or simply dict
in Python 3.9+) to declare a key:value
pair. However, in this way, you can't use custom validations for various attributes in your expected JSON
, as you would do with Pydantic models or Body fields (e.g., check if an email address is valid, or if a string follows a specific pattern).
from typing import Dict, Any
@app.post("/")
def main(user: Dict[Any, Any]): # or, user: dict
return user
If you are confident that the incoming data is a valid JSON
, you can use Starlette's Request
object directly to get the request body parsed as JSON
, using await request.json()
. However, with this approach, not only can't you use custom validations for your attributes, but you would also need to define your endpoint with async def
, since request.json()
is an async
method and thus, one needs to await
it (have a look at this answer for more details on def
vs async def
).
from fastapi import Request
@app.post("/")
async def main(request: Request):
return await request.json()
If you wish, you could also implement some checking of the Content-Type
in the request's headers, before attempting to parse the data, similar to this answer. However, just because a request says application/json
in the headers, doesn't always mean that this is true, or that the incoming data is a valid JSON
(i.e., may be missing a curly bracket, have a key that does not have a value, etc). Hence, you could use a try-except
block when you attempt to parse the data, letting you handle any JSONDecodeError
, in case there is an issue with the way in which your JSON
data is formatted.
from fastapi import Request
@app.post("/")
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.'
import requests
url = 'http://127.0.0.1:8000'
payload ={"user": "foo"}
resp = requests.post(url=url, json=payload)
print(resp.json())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With