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

标签 python rest request fastapi forward

我有一个用于测试/开发目的的 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()

最佳答案

您可以使用AsyncClient()来自httpx库,如 this answer 中所述,以及this answerthis answer (查看这些答案,了解有关下面演示的方法的更多详细信息)。您可以在 startup event handler 内生成一个 Client ,将其存储在 app 实例上 - 如所述 here ,以及herehere -并在每次需要时重复使用它。完成后,您可以使用shutdown event handler显式地关闭Client。 .

工作示例

主服务器

当构建即将转发到其他服务器的请求时,主服务器使用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对象用于读取正文,但您可以不定义UploadFileForm和其他参数照常。在下面的示例中,服务器正在监听端口 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)

关于python - 如何将FastAPI请求转发到另一台服务器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74555102/

相关文章:

java - c :out tag did not show result

java - 在java代码中提交表单时request.getParameter()获取null?

python - Pandas:从返回数据创建索引时间序列 [从 100 开始]

python - 如何优化代码以迭代 dict 并将值存储在列表中?

python - BeautifulSoup - 替换字符串不起作用

java - 无法在异常处理程序中捕获 JsonMappingException

rest - 如何处理REST API中新对象属性的引入

python - 使用 python 从没有表单标签但有文本输入的网站中抓取数据

javascript - 使用ajax从 Node js服务器获取json信息

mysql - 什么对表演更好 : IN or OR