The curl -I
option (which is used in the example you provided) is the same as using curl --head
and performrs an HTTP HEAD
request, in order to fetch the headers only (not the body/content of the resource):
The HTTP HEAD
method requests the headers that would be
returned if the HEAD
request's URL was instead requested with the
HTTP GET
method. For example, if a URL might produce a large
download, a HEAD
request could read its Content-Length
header
to check the filesize without actually downloading the file.
The requested resource/endpoint you are trying to call supports only GET
requests; hence, the 405 Method Not Allowed
response status code, which indicates that the server knows the request method, but the target resource doesn't support this method.
To demonstrate this, have a look at the example below:
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
async def main():
return {'Hello': 'World'}
Test using Python requests (similar result is obtained using curl -I http://127.0.0.1:8000
)
import requests
# Making a GET request
# r = requests.get('http://127.0.0.1:8000')
# Making a HEAD request
r = requests.head('http://127.0.0.1:8000')
# check status code for response received
print(r.status_code, r.reason)
# print headers of request
print(r.headers)
# checking if request contains any content
print(r.content)
Output (indicating by the allow
response header which request methods are supported by the requested resource):
405 Method Not Allowed
{'date': 'Sun, 12 Mar 2023', 'server': 'uvicorn', 'allow': 'GET', 'content-length': '31', 'content-type': 'application/json'}
b''
If, instead, you performed a GET
request (in order to issue a GET
request in the example above, uncomment the line for GET
request and comment the one for HEAD
request, or in curl
use curl http://127.0.0.1:8000
), the response would be as follows:
200 OK
{'date': 'Sun, 12 Mar 2023', 'server': 'uvicorn', 'content-length': '17', 'content-type': 'application/json'}
b'{"Hello":"World"}'
Solutions
To make a FastAPI endpoint supporting more than one HTTP request methods (e.g., both GET
and HEAD
requests), the following solutions are available.
Solution 1
Cretate two different functions with a decorator refering to the request method for which they should be called, i.e., @app.head()
and @app.get()
, or, if the logic of the function is the same for both the request methods, then simply add a decorator for each request method that you would like the endpoint to support to the same function. For instance:
from fastapi import FastAPI
app = FastAPI()
@app.head('/')
@app.get('/')
async def main():
return {'msg': 'Hello World'}
Solution 2
Use the @app.api_route()
decorator, which allows you to define the set of supported request methods for the endpoint. For example:
from fastapi import FastAPI
app = FastAPI()
@app.api_route('/', methods=['GET', 'HEAD'])
async def main():
return {'msg': 'Hello World'}
Output
Both solutions above would respond as follows (when a HEAD
request is issued by a client):
200 OK
{'date': 'Sun, 12 Mar 2023', 'server': 'uvicorn', 'content-length': '17', 'content-type': 'application/json'}
b''
In both solutions, if you would like to distinguish between the methods used to call the endpoint, you could use the .method
attribute of the Request
object. For instance:
from fastapi import FastAPI, Request
app = FastAPI()
@app.api_route('/', methods=['GET', 'HEAD'])
async def main(request: Request):
if request.method == 'GET':
print('GET method was used')
elif request.method == 'HEAD':
print('HEAD method was used')
return {'msg': 'Hello World'}
Note 1: In the test example using Python requests above, please avoid calling the json()
method on the response when performing a HEAD
request, as a response to a HEAD
method request does not normally have a body, and it would raise an exception, if attempted calling r.json()
—see this answer for more details. Instead, use print(r.content)
, as demonstrated in the example above on HEAD
requests, if you would like to check whether or not there is a response body.
Note 2: The 405 Method Not Allowed
response may also be caused by other reasons—see related answers here and here, as well as here and here.