How to re-route requests to a different URL/endpoint in FastAPI?
Question:
I am trying to write a middleware in my FastAPI application, so that requests coming to endpoints matching a particular format will be re-routed to a different URL, but I am unable to find a way to do that since request.url
is read-only.
I am also looking for a way to update request headers before re-routing.
Are these things even possible in FastAPI?
Redirection is the best I could do so far:
from fastapi import Request
from fastapi.responses import RedirectResponse
@app.middleware("http")
async def redirect_middleware(request: Request, call_next):
if matches_certain_format(request.url.path):
new_url = create_target_url(request.url.path)
return RedirectResponse(url=new_url)
Answers:
Yes, FastAPI applications allow for URL rewriting. However, as you mentioned, the request.url is immutable, so you are unable to update it.
Instead, you can make a new URL object and pass it to the request object with the updated URL and headers. Here is an example middleware that does both URL rewriting and header modification:
from fastapi import Request
from fastapi.responses import RedirectResponse
@app.middleware("http")
async def redirect_middleware(request: Request, call_next):
if matches_certain_format(request.url.path):
new_url = create_target_url(request.url.path)
headers = request.headers.copy()
headers['new-header'] = 'new-value'
new_request = Request(request.scope, request.receive, request.app, request.method, URL(new_url), headers=headers)
return await call_next(new_request)
else:
response = await call_next(request)
return response
To change the request
‘s URL path—in other words, re-route the request to a different endpoint—one can simply modify the request.scope['path']
value inside the middleware, before processing the request, as demonstrated in Option 3 of this answer. If your API endpoints include path parameters (e.g., '/users/{user_id}'
), then you mgiht want to have a look at this answer on how to extract that kind of path from the request
object, and then compare it against a pre-defined list of routes_to_reroute
, as shown below.
As for updating the request headers, or adding new custom headers to the request, you can follow a similar approach to the one described here, which demonstrates how to modify the request.scope['headers']
value.
Working Example
If you would like to avoid maintaining a list of routes to re-route and performing checks inside the middleware, you could instead mount a sub-application, which will contain only the routes that require re-routing, and add the middleware to that sub-app, similar to Option 3 of this answer.
from fastapi import FastAPI, Request
app = FastAPI()
routes_to_reroute = ['/']
@app.middleware('http')
async def some_middleware(request: Request, call_next):
if request.url.path in routes_to_reroute:
request.scope['path'] = '/welcome'
headers = dict(request.scope['headers'])
headers[b'custom-header'] = b'my custom header'
request.scope['headers'] = [(k, v) for k, v in headers.items()]
return await call_next(request)
@app.get('/')
async def main():
return 'OK'
@app.get('/welcome')
async def welcome(request: Request):
return {'msg': 'Welcome!', 'headers': request.headers}
I am trying to write a middleware in my FastAPI application, so that requests coming to endpoints matching a particular format will be re-routed to a different URL, but I am unable to find a way to do that since request.url
is read-only.
I am also looking for a way to update request headers before re-routing.
Are these things even possible in FastAPI?
Redirection is the best I could do so far:
from fastapi import Request
from fastapi.responses import RedirectResponse
@app.middleware("http")
async def redirect_middleware(request: Request, call_next):
if matches_certain_format(request.url.path):
new_url = create_target_url(request.url.path)
return RedirectResponse(url=new_url)
Yes, FastAPI applications allow for URL rewriting. However, as you mentioned, the request.url is immutable, so you are unable to update it.
Instead, you can make a new URL object and pass it to the request object with the updated URL and headers. Here is an example middleware that does both URL rewriting and header modification:
from fastapi import Request
from fastapi.responses import RedirectResponse
@app.middleware("http")
async def redirect_middleware(request: Request, call_next):
if matches_certain_format(request.url.path):
new_url = create_target_url(request.url.path)
headers = request.headers.copy()
headers['new-header'] = 'new-value'
new_request = Request(request.scope, request.receive, request.app, request.method, URL(new_url), headers=headers)
return await call_next(new_request)
else:
response = await call_next(request)
return response
To change the request
‘s URL path—in other words, re-route the request to a different endpoint—one can simply modify the request.scope['path']
value inside the middleware, before processing the request, as demonstrated in Option 3 of this answer. If your API endpoints include path parameters (e.g., '/users/{user_id}'
), then you mgiht want to have a look at this answer on how to extract that kind of path from the request
object, and then compare it against a pre-defined list of routes_to_reroute
, as shown below.
As for updating the request headers, or adding new custom headers to the request, you can follow a similar approach to the one described here, which demonstrates how to modify the request.scope['headers']
value.
Working Example
If you would like to avoid maintaining a list of routes to re-route and performing checks inside the middleware, you could instead mount a sub-application, which will contain only the routes that require re-routing, and add the middleware to that sub-app, similar to Option 3 of this answer.
from fastapi import FastAPI, Request
app = FastAPI()
routes_to_reroute = ['/']
@app.middleware('http')
async def some_middleware(request: Request, call_next):
if request.url.path in routes_to_reroute:
request.scope['path'] = '/welcome'
headers = dict(request.scope['headers'])
headers[b'custom-header'] = b'my custom header'
request.scope['headers'] = [(k, v) for k, v in headers.items()]
return await call_next(request)
@app.get('/')
async def main():
return 'OK'
@app.get('/welcome')
async def welcome(request: Request):
return {'msg': 'Welcome!', 'headers': request.headers}