Search code examples
pythonheaderfastapiquery-stringstarlette

How to update/modify request headers and query parameters in a FastAPI middleware?


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?


Solution

  • 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.

    Working Example

    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}