如何在 FastAPI 中将请求重新路由到不同的 URL/端点?

How to re-route requests to a different URL/endpoint in FastAPI?

提问人:Aniket Tiratkar 提问时间:3/14/2023 最后编辑:Brian Tompsett - 汤莱恩Aniket Tiratkar 更新时间:11/19/2023 访问量:2296

问:

我正在尝试在我的 FastAPI 应用程序中编写一个中间件,以便来自与特定格式匹配的端点的请求将被重新路由到不同的 URL,但我无法找到一种方法来做到这一点,因为是只读的。request.url

我也在寻找一种在重新路由之前更新请求标头的方法。

这些事情在 FastAPI 中是否可能?

重定向是迄今为止我能做的最好的:

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)
python fastapi 中间件 starlette

评论

1赞 Libra 3/14/2023
您能否像浏览器中的某个人一样请求另一个 URL,并返回它返回的任何内容?localhost
0赞 Aniket Tiratkar 3/14/2023
@Libra,那行得通,我想我现在会采用这种方法。不过,我希望 FastAPI 能提供更好的方法。
0赞 Chris 3/14/2023
这回答了你的问题吗?如何创建可以接受表单或JSON正文的FastAPI端点?

答:

0赞 SyntaxNavigator 3/14/2023 #1

是的,FastAPI 应用程序允许 URL 重写。但是,正如您提到的,request.url 是不可变的,因此您无法更新它。

相反,您可以创建一个新的 URL 对象,并使用更新的 URL 和标头将其传递给请求对象。下面是一个同时执行 URL 重写和标头修改的中间件示例:

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

评论

1赞 Aniket Tiratkar 3/14/2023
这是行不通的。首先,会失败,因为标头没有方法。假设我们在这里使用,那么它将失败,因为它是只读的。谢谢你的回答。request.headers.copy()copycopy.copyheaders['new-header'] = 'new-value'
0赞 cactus 11/23/2023
我实际上尝试了这种方法,只修改 URL 而不是标头,以创建new_request并得到: TypeError: __init__() 得到了一个意外的关键字参数“headers”。但是标头还包含“host”标头,我猜也需要将其修改为新 URL......而且确实没有 copy() 方法
3赞 Chris 3/14/2023 #2

要更改 的 URL 路径(换句话说,将请求重新路由到其他终结点),只需在处理请求之前修改中间件中的值即可,如本答案的选项 3 所示。如果您的 API 端点包含路径参数(例如,),那么您希望查看有关如何从对象中提取此类路径的答案,然后将其与预定义的 列表进行比较,如下所示。requestrequest.scope['path']'/users/{user_id}'requestroutes_to_reroute

至于更新请求标头或向请求添加新的自定义标头,您可以遵循与此处描述的方法类似的方法,该方法演示了如何修改值。request.scope['headers']

工作实例

如果您希望避免维护要重新路由的路由列表并在中间件中执行检查,则可以改为挂载一个子应用程序,该子应用程序将仅包含需要重新路由的路由,并将中间件添加到该子应用程序,类似于此答案的选项 3。

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}

评论

0赞 Aniket Tiratkar 3/14/2023
这看起来不错,我认为它会起作用,但不符合我的要求(在原始问题中没有提到)。可能是因为我试图将请求重新路由到其他服务器吗?我尝试用此语句更改服务器,但它没有离开服务器。你知道这里可能出了什么问题吗?request.scope['server'] = (host, int(port))
0赞 Chris 3/14/2023
对于将请求重定向/转发到其他服务器,您可以继续使用(但它不允许您将标头传递给其他服务器 - 请参阅此答案,以及答案和此答案以获取更多详细信息;除非请求是在前端使用 JS 发出的,这将允许您解决此问题 - 请参阅此处)...RedirectResponse
0赞 Chris 3/14/2023
....或将请求转发到其他服务器,类似于此答案中演示的方法。
1赞 Aniket Tiratkar 3/15/2023
非常感谢这些链接,它准确地给了我我需要的信息。我同意你对这个问题的建议,我将保持原样。