如何将FastAPI请求转发到另一台服务器?

问题描述 投票:0回答:1

我有一个用于测试/开发目的的 FastAPI 应用程序。我想要的是,到达我的应用程序的任何请求都会自动发送到另一台服务器上的另一个应用程序,具有完全相同的参数和相同的端点。这不是重定向,因为我仍然希望应用程序像往常一样处理请求并返回值。我只是想向不同服务器上的不同版本的应用程序发起类似的请求,而不需要等待其他服务器的答复,以便其他应用程序获取该请求,就像原始请求发送给它一样。 我怎样才能实现这一目标?以下是我用于处理请求的示例代码:

@app.post("/my_endpoint/some_parameters") def process_request( params: MyParamsClass, pwd: str = Depends(authenticate), ): # send the same request to http://my_other_url/my_endpoint/ return_value = process_the_request(params) return return_value.as_json()


python rest request fastapi forward
1个回答
5
投票

AsyncClient()

 库中的 
httpx
,如
这个答案
中所述,以及这个答案这个答案(请查看这些答案以获取更多详细信息)下面演示的方法)。您可以在 Client
 事件处理程序
内生成一个 startup
,将其存储在 
app
实例上(如
here
herehere 中所述),并在每次需要时重复使用它它。完成后,您可以使用 close
 事件处理程序
显式地
Client
shutdown
更新:
由于 startup
shutdown
事件已不再被弃用,并且可能会在将来,请查看
这个答案
,了解如何使用 lifespan 事件处理程序)。
工作示例

主服务器

在构建即将转发到其他服务器的请求时,主服务器使用

request.stream()

从客户端的请求中读取请求

body
,它提供了一个
async
迭代器,这样如果客户端发送了一个请求具有一些大的正文(例如,客户端上传一个大文件),主服务器在转发请求之前不必等待整个
body
被接收并加载到内存中,如果您这样做,就会发生这种情况使用
await request.body()
代替,如果
body
无法装入 RAM,可能会导致服务器问题。
您可以按照下面定义的 

/upload

相同的方式添加多个路由,指定路径以及端点的 HTTP 方法。请注意,下面的

/upload
路线使用 Starlette 的
path
转换器来捕获任意路径,如
here
here 所示。如果您愿意,您还可以指定确切的路径参数,但如果路径参数太多,下面提供了更方便的方法。无论如何,路径将根据下面其他服务器中的端点进行评估,您可以在其中显式指定路径参数。 from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse from starlette.background import BackgroundTask import httpx app = FastAPI() @app.on_event('startup') async def startup_event(): client = httpx.AsyncClient(base_url='http://127.0.0.1:8001/') # this is the other server app.state.client = client @app.on_event('shutdown') async def shutdown_event(): client = app.state.client await client.aclose() async def _reverse_proxy(request: Request): client = request.app.state.client url = httpx.URL(path=request.url.path, query=request.url.query.encode('utf-8')) req = client.build_request( request.method, url, headers=request.headers.raw, content=request.stream() ) r = await client.send(req, stream=True) return StreamingResponse( r.aiter_raw(), status_code=r.status_code, headers=r.headers, background=BackgroundTask(r.aclose) ) app.add_route('/upload/{path:path}', _reverse_proxy, ['POST']) if __name__ == '__main__': import uvicorn uvicorn.run(app, host='0.0.0.0', port=8000)

其他服务器

再次强调,为了简单起见,

Request

对象用于读取正文,但您可以像往常一样定义

UploadFile
Form
和其他参数。在下面的示例中,服务器正在侦听端口
8001
from fastapi import FastAPI, Request

app = FastAPI()

@app.post('/upload/{p1}/{p2}')
async def upload(p1: str, p2: str, q1: str, request: Request):
    return {'p1': p1, 'p2': p2, 'q1': q1, 'body': await request.body()}
    
    
if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app, host='0.0.0.0', port=8001)

使用 
httpx

 测试上述示例
import httpx url = 'http://127.0.0.1:8000/upload/hello/world' files = {'file': open('file.txt', 'rb')} params = {'q1': 'This is a query param'} r = httpx.post(url, params=params, files=files) print(r.content)

© www.soinside.com 2019 - 2024. All rights reserved.