I want to make an HTTP
endpoint in FastAPI that requires a specific Header
, produces a custom response
code when the Header
is absent, as well as shows the Header
as required in the OpenAPI docs generated by FastAPI.
For example, if I make this endpoint to require some-custom-header
:
@app.post("/")
async def fn(some_custom_header: str = Header(...)):
pass
when a client request lacks some-custom-header
, the server will produce a response
with error code 422 Unprocessable entity
. However I'd like to be able to change that to 401 Unauthorized
. In other words, I would like to customise the RequestValidationError
for that specific route in my API.
I thought a possible solution would be to use Header(None)
, and do a test for None
in the function body, but, unfortunately, this results in the OpenAPI docs indicating that the header is optional.
Option 1
If you didn't mind having the
Header
showing asOptional
in OpenAPI/Swagger UI autodocs, it would be as easy as follows:Option 2
However, since you would like the
Header
to appear as required in OpenAPI, you should override the default exception handler. When a request contains invalid data, FastAPI internally raises aRequestValidationError
. Thus, you need to override theRequestValidationError
, which contains the body it received with invalid data.Since
RequestValidationError
is a sub-class of Pydantic'sValidationError
, you can access the errors as shown in the link above, so that you can check whether your customHeader
is included in the errors (if so, that means that is either missing from the request, or is not ofstr
type), and hence, return your custom error response. If your customHeader
(i.e.,some_custom_header
in the example below) is the only parameter in that specific endpoint, then it is not necessary to perform the check described above (and demosntrated below), as if aRequestValidationError
was raised, it would be only for that parameter.Example
Option 3
Another solution would be to use Sub-Application(s) (inspired by the discussion here). You could create a sub app (or more if needed) and mount it to the main app—which would include the route(s) that require the custom
Header
; hence, overriding theexception_handler
forRequestValidationError
in that sub app would only apply to those routes, without having to check for therequest.url.path
, as demonstrated in the previous solution—and have the main app with the remaining routes as usual. As per the documentation:Example
Note: If you mounted the sub-application (i.e.,
subapi
in the example below) using the'/'
path, you wouldn't be able to access the routes ofsubapi
at http://127.0.0.1:8000/docs, as the API docs on that page will only include the routes of the main app. Also, it would interfere with the'/'
route of the main API (if such a route exists in the main API), and since endpoints' order matters in FastAPI, issuing a request tohttp://127.0.0.1:8000/
would actually call the corresponding route of the main API (as demonstrated below). Thus, you would rather mountsubapi
using a different path, e.g.,'/sub'
, as demonstrated below, and access the sub API docs at http://127.0.0.1:8000/sub/docs. A Python requests example is also given below, demonstrating how to test the app.Test the example above
Option 4
A further solution would be to use an
APIRouter
with a customAPIRoute
class, as demonstrated in Option 2 of this answer, and handle the request inside atry-except
block (which will be used to catchRequestValidationError
exceptions), as described in FastAPI's documentation. If an exception occurs, you can handle the error as desired, and return a custom respone.Example
Test the example above