Here's a self-contained minimal, reproducible, example with my use case:
from fastapi import FastAPI
import pytest
from fastapi.testclient import TestClient
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
client = TestClient(app)
def test_index_head():
response = client.head("/")
assert response.status_code == 200
@app.head and a helper method for each route, but it's cumbersome:@app.head("/")
def read_root_head():
return Response()
encode/starlette#45 is only about static FileResponse. I don't know how a default should be set for a dynamic API.
Not so, if you look at the related commit at https://github.com/encode/starlette/pull/132/files in starlette/routing.py, you can see Starlette adds HEAD methods automatically for every Route that supports GET, regardless of whether they are FileResponse or not.
Many web frameworks do this by default too, e.g. Flask (https://flask.palletsprojects.com/en/1.1.x/quickstart/#http-methods) and Django. It makes sense to do that because a server should respond to an HTTP HEAD request as defined in the spec, see e.g.:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD
"The HTTP HEAD method requests the headers that are returned if the specified resource would be requested with an HTTP GET method. Such a request can be done before deciding to download a large resource to save bandwidth, for example."
Clients use this for cache invalidation. The server just needs to run the route method, respond with the headers but discard the response body. Ideally, FastAPI would automatically add an OPTIONS method handler, too. This would help make FastAPI a first class web framework.
This is safe even for a dynamic API, because to follow REST principles, HTTP GET routes should always be idempotent and have no side-effects (as should HEAD, PUT, DELETE, OPTIONS and TRACE). Only HTTP POSTs are allowed to have side effects (see https://tools.ietf.org/html/rfc7231#section-4.2.2).
This behavior could be controlled by a flag defaulting to true.
working on this
Thanks @matthewlloyd for the very clear argument and especially for the self-contained example, that helps a lot.
Indeed, here in the spec: https://tools.ietf.org/html/rfc7231#section-4.3.2 it says:
The server SHOULD send the same header fields in response to a HEAD request as it would have sent if the request had been a GET [...]
And the OpenAPI spec doesn't say anything contradicting it, so FastAPI should support it.
I think ideally, it should handle HEAD requests automatically for GET requests, without adding them to the OpenAPI schema. I think HEAD operations should be added to OpenAPI only when explicitly declared in the code (ideally).
Thanks @victorphoenix3 for working on this! Let me know if you need any help with it.
As a side note, today, when making a HEAD request, you get a 405 returned, which according to spec*, should include an "Allow" header. I'm not if this is FastAPI or an underlying library returning this 405 - but maybe it should be changed as well.
Most helpful comment
Not so, if you look at the related commit at https://github.com/encode/starlette/pull/132/files in
starlette/routing.py, you can see Starlette addsHEADmethods automatically for everyRoutethat supportsGET, regardless of whether they areFileResponseor not.Many web frameworks do this by default too, e.g. Flask (https://flask.palletsprojects.com/en/1.1.x/quickstart/#http-methods) and Django. It makes sense to do that because a server should respond to an
HTTP HEADrequest as defined in the spec, see e.g.:https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD
"The HTTP HEAD method requests the headers that are returned if the specified resource would be requested with an HTTP GET method. Such a request can be done before deciding to download a large resource to save bandwidth, for example."
Clients use this for cache invalidation. The server just needs to run the route method, respond with the headers but discard the response body. Ideally, FastAPI would automatically add an OPTIONS method handler, too. This would help make FastAPI a first class web framework.
This is safe even for a dynamic API, because to follow REST principles, HTTP GET routes should always be idempotent and have no side-effects (as should HEAD, PUT, DELETE, OPTIONS and TRACE). Only HTTP POSTs are allowed to have side effects (see https://tools.ietf.org/html/rfc7231#section-4.2.2).
This behavior could be controlled by a flag defaulting to true.