-1

I have the following FastAPI application:

from pydantic import BaseModel as Schema
from fastapi import FastAPI

api = FastAPI()

class User(Schema):
    firstname: str
    lastname: str
    age: int | None = None


@api.post('/user')
def user_selection(user: User):
    return {'data': f'{user.firstname} {user.lastname} age: {user.age}'}

The main file is called file.py, so I run the uvicorn server like this:

uvicorn file:api --reload

Through another console, I send this request:

curl -X 'POST' -i 'http://127.0.0.1:8000/user' -d '{firstname":"mike", "lastname":"azer"}'

but, I get this error:

HTTP/1.1 422 Unprocessable Entity
date: Sun, 05 Feb 2023 16:01:14 GMT
server: uvicorn
content-length: 88
content-type: application/json

{"detail":[{"loc":["body"],"msg":"value is not a valid dict","type":"type_error.dict"}]}

Why is that?

If, however, I set the Content-Type header to application/json in my request:

curl -X 'POST'   'http://127.0.0.1:8000/user'   -H 'Content-Type: application/json'   -d '{
  "firstname": "aaa",
  "lastname": "zzz"
}'

it works just fine.

Why do I need the header? When I do a GET request, I don't have to add a header and it works. What's the difference with the POST request?

Chris
  • 18,724
  • 6
  • 46
  • 80
Luc
  • 11
  • 2
  • `When I do a get request, i don't have header` When you do a GET request you don't have a body either. `why do i need a header?` To tell the server what kind of data you're sending. – tkausl Feb 05 '23 at 16:09
  • 2
    `-d '{firstname":"mike", "lastname":"azer"}'` You're missing a double quote mark before `firstname`. – John Gordon Feb 05 '23 at 16:09

1 Answers1

1

Q: "Why Content-Type header is required in a JSON POST request?"

A: According to curl's documentation:

POSTing with curl's -d option will make it include a default header that looks like Content-Type: application/x-www-form-urlencoded. That is what your typical browser will use for a plain POST.

Many receivers of POST data do not care about or check the Content-Type header.

If that header is not good enough for you, you should, of course, replace that and instead provide the correct one. Such as if you POST JSON to a server and want to more accurately tell the server about what the content is:

curl -d '{json}' -H 'Content-Type: application/json' https://example.com

Hence, the reason for 'Content-Type: application/json' header being required when sending a POST request containing JSON data is simply because curl by default uses application/x-www-form-urlencoded Content-Type to encode the data that forms the body of the request—this is what a browser typically uses when submitting an HTML <form>.

If, however, files are also included in the request, then multipart/form-data content type is automatically used by curl—the same applies to using Python requests, as shown in this answer (as for HTML forms, you need to manually specify the enctype, as demonstrated here)—as per curl's documentation:

Posting binary

When reading data to post from a file, -d will strip out carriage return and newlines. Use --data-binary if you want curl to read and use the given file in binary exactly as given:

curl --data-binary @filename http://example.com/ 

I would also suggest you to take a look at related answers here, here and here, as well as use the interactive Swagger UI autodocs at /docs for testing the API, which would also automatically generate the curl command for testing an API endpoint.

Q: "When I do a GET request, I don't have to add a header and it works. What's the difference with the POST request?"

A: When issuing a GET request, there is no body included, and hence, no body contents to encode and no need to tell the server what kind of data are being send. In GET requests all parameters must appear in the URL (i.e., the query string) or in a header. While the HTTP standard doesn't define a limit for how long URLs or headers can be, there is usually a length limit dependent on both the server and the client (usually between 2KB and 8KB). In a POST request, however, the limit is much higher and more dependent on the server than the client.

Chris
  • 18,724
  • 6
  • 46
  • 80