I'm trying to write a middleware for a FastAPI project that manipulates the request headers
and / or query
parameters in some special cases.
I've managed to capture and modify the request object in the middleware, but it seems that even if I modify the request object that is passed to the middleware, the function that serves the endpoint receives the original, unmodified request.
Here is a simplified version of my implementation:
from fastapi import FastAPI, Request
from starlette.datastructures import MutableHeaders, QueryParams
from starlette.middleware.base import BaseHTTPMiddleware
class TestMiddleware(BaseHTTPMiddleware):
def __init__(self, app: FastAPI):
super().__init__(app)
def get_modified_query_params(request: Request) -> QueryParams:
pass ## Create and return new query params
async def dispatch(
self, request: Request, call_next, *args, **kwargs
) -> None:
# Check and manipulate the X-DEVICE-TOKEN if required
header_key = "X-DEVICE-INFo"
new_header_value = "new device info"
new_header = MutableHeaders(request._headers)
new_header[header_key] = new_header_value
request._headers = new_header
request._query_params = self.get_modified_query_params(request)
print("modified headers =>", request.headers)
print("modified params =>", request.query_params)
return await call_next(request)
Even though I see the updated values in the print statements above, when I try to print request object in the function that serves the endpoint, I see original values of the request.
What am I missing?
To update or modify the request headers within a middleware, you would have to update request.scope['headers']
, as described in this answer. In that way, you could add new custom headers, as well as modify existing ones. In a similar way, by updating request.scope['query_string']
, you could modify existing, as well as add new, query parameters. A working example is given below.
from fastapi import FastAPI, Request
from urllib.parse import urlencode
app = FastAPI()
@app.middleware('http')
async def some_middleware(request: Request, call_next):
# update request headers
headers = dict(request.scope['headers'])
headers[b'custom-header'] = b'my custom header'
request.scope['headers'] = [(k, v) for k, v in headers.items()]
# update request query parameters
q_params = dict(request.query_params)
q_params['custom-q-param'] = 'my custom query param'
request.scope['query_string'] = urlencode(q_params).encode('utf-8')
return await call_next(request)
@app.get('/')
async def main(request: Request):
return {'headers': request.headers, 'q_params': request.query_params}