我想将对我的 Flask 应用程序发出的请求代理到机器上本地运行的另一个 Web 服务。我宁愿为此使用 Flask,也不愿使用我们更高级别的 nginx 实例,这样我们就可以重用我们应用程序中内置的现有身份验证系统。我们越能保持这种“单点登录”就越好。
是否有现有的模块或其他代码来执行此操作?试图将 Flask 应用程序连接到 httplib 或 urllib 之类的东西被证明是一种痛苦。
我花了很多时间在同一件事上工作,并最终找到了一个使用请求库的解决方案,看起来效果很好。它甚至可以处理在一个响应中设置多个 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.com'),
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 月更新:可能应包括RFC 2616 第 13.5.1 节excluded_headers
定义的所有“逐跳标头” 。
我在基于 Werkzeug 的应用程序中使用 httplib 实现了代理(在您的情况下,我需要使用 webapp 的身份验证和授权)。
尽管 Flask 文档没有说明如何访问 HTTP 标头,但您可以使用request.headers
(参见Werkzeug 文档)。如果您不需要修改响应,并且代理应用程序使用的标头是可预测的,那么代理是直接的。
请注意,如果您不需要修改响应,则应使用werkzeug.wsgi.wrap_file
包装 httplib 的响应流。这允许将开放的操作系统级文件描述符传递给 HTTP 服务器以获得最佳性能。
我最初的计划是让面向公众的 URL 类似于http://www.example.com/admin/myapp
代理到http://myapp.internal.example.com/
. 沿着这条路走下去会导致疯狂。
大多数 webapp,尤其是自托管的,都假设它们将在 HTTP 服务器的根目录下运行,并执行诸如通过绝对路径引用其他文件之类的操作。要解决这个问题,您必须在各处重写 URL:位置标头和 HTML、JavaScript 和 CSS 文件。
我确实写了一个 Flask 代理蓝图来做到这一点,虽然它对于我真正想要代理的一个 web 应用程序来说足够好,但它是不可持续的。这是一大堆正则表达式。
最后,我在 nginx 中设置了一个新的虚拟主机,并使用了它自己的代理。由于两者都位于主机的根目录,因此几乎不需要重写 URL。(几乎不需要什么,处理的是 nginx 的代理模块。)被代理的 webapp 进行自己的身份验证,这对于现在来说已经足够好了。