使用 Flask 代理到另一个 Web 服务

我想代理请求我的 Flask 应用程序到另一个 Web 服务本地运行的机器上。我宁愿使用 Flask 而不是更高级的 nginx 实例,这样我们就可以重用内置在应用程序中的现有身份验证系统。我们越能保持这个“单一标志”越好。

是否有现有的模块或其他代码来执行此操作?尝试将 Flask 应用程序连接到 httplib 或 urllib 等应用程序被证明是一件痛苦的事情。

51862 次浏览

我在一个基于 Werkzeug 的应用程序中使用 httplib 实现了一个代理(在您的例子中,我需要使用 webapp 的身份验证和授权)。

虽然 Flask 文档没有说明如何访问 HTTP 头,但是您可以使用 request.headers(参见 沃克泽格文件)。如果您不需要修改响应,并且被代理的应用程序使用的头是可预测的,那么代理非常简单。

注意,如果不需要修改响应,应该使用 werkzeug.wsgi.wrap_file来包装 httplib 的响应流。这允许将打开的 OS 级文件描述符传递到 HTTP 服务器以获得最佳性能。

我最初的计划是将面向公众的 URL 设置为类似于 http://www.example.com/admin/myapp代理到 http://myapp.internal.example.com/的东西。沿着这条路走下去会引发疯狂。

大多数 webapps,特别是自托管的 webapps,假设它们将在 HTTP 服务器的根目录下运行,并通过绝对路径引用其他文件。为了解决这个问题,您必须重写所有地方的 URL: 位置标题和 HTML、 JavaScript 和 CSS 文件。

我做了 写一个烧瓶代理蓝图这样做,虽然它工作得很好,为一个网络应用程序,我真的想代理,它是不可持续的。这是一个正则表达式的大混乱。

最后,我在 nginx 中设置了一个新的虚拟主机,并使用了它自己的代理。由于两者都是主机的根,因此 URL 重写基本上是不必要的。(只需要处理 nginx 的代理模块就可以了。)被代理的网络应用程序自己进行认证,这对于现在来说已经足够好了。

我花了很多时间在同一件事情上,并最终找到了一个解决方案,使用 请求库,似乎工作得很好。它甚至可以在一个响应中设置多个 cookie,这需要一些调查才能弄清楚。下面是烧瓶视图功能:

from flask import request, Response
import requests


def _proxy(*args, **kwargs):
resp = requests.request(
method=request.method,
url=request.url.replace(request.host_url, 'new-domain.example'),
headers={key: value for (key, value) in request.headers if key != 'Host'},
data=request.get_data(),
cookies=request.cookies,
allow_redirects=False)


excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
headers = [(name, value) for (name, value) in resp.raw.headers.items()
if name.lower() not in excluded_headers]


response = Response(resp.content, resp.status_code, headers)
return response

2021年4月更新: excluded_headers可能应该包括由 RFC 2616章节13.5.1定义的所有“逐跳头”。