ChatGPT解决这个技术问题 Extra ChatGPT

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

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

是否有现有的模块或其他代码来执行此操作?试图将 Flask 应用程序连接到 httplib 或 urllib 之类的东西被证明是一种痛苦。

在为不支持跨域安全的旧浏览器(如 IE7)执行 AJAX 服务时,这个问题也很重要。
您对 httplib 有什么具体问题?
@jd:鉴于烧瓶位于 WSGI 的应用程序端,我不确定我是否能够有效地转发所有数据。例如,Flask 请求对象似乎不包含我想要传递给 httplib 的原始请求(甚至请求标头)。并不是不可能,这只是一种痛苦,我希望现有的模块已经做到了。

S
Stephen Ostermiller

我花了很多时间在同一件事上工作,并最终找到了一个使用请求库的解决方案,看起来效果很好。它甚至可以处理在一个响应中设置多个 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 section 13.5.1 定义的所有“逐跳标头”。


@Evan 不错的解决方案。但是,它不处理 3xx 重定向,因为重定向 url 可能指向代理主机
有人可以在 MWE 应用程序中添加您如何称呼它吗?
这太好了,非常感谢! (这是让 ngrok 同时处理前端和后端所需要的。)但是,对我来说 request.host_url 包括 http:// 和一个斜杠,所以我的替换行是:request.url.replace(request.host_url, 'http://new-domain.com/')
@Ire我遇到了这个问题并添加了一个编辑来修复。我所做的只是用 headers = [(name, value) if (name.lower() != 'location') else (name, value.replace('http://new-domain.com/', request.host_url)) for (name, value) in resp.raw.headers.items() if name.lower() not in excluded_headers] 替换了标题过滤器行。这只是修复了 Location 标头中的 URL。 (感谢@jbasko 指出斜杠的问题)
您还可以流式传输响应内容,而不是完全在服务器上读取它。为此,将上面的 resp.content 替换为 resp.iter_content(chunk_size=10*1024) 并将 content_type=r.headers['Content-Type'] 参数添加到 Response 构造函数。
j
jd.

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

尽管 Flask 文档没有说明如何访问 HTTP 标头,但您可以使用 request.headers(请参阅 Werkzeug documentation)。如果您不需要修改响应,并且代理应用程序使用的标头是可预测的,那么代理是直接的。

请注意,如果您不需要修改响应,则应使用 werkzeug.wsgi.wrap_file 包装 httplib 的响应流。这允许将开放的操作系统级文件描述符传递给 HTTP 服务器以获得最佳性能。


谢谢,今天下午我破解了一些东西。但是,由于 httplib 不能很好地处理它们,因此 cookie 存在各种问题。不幸的是,我认为我需要修改响应以进行一些简单的 URL 重写(即
在我的情况下,只有一个 cookie 需要捕获,因此正则表达式完成了解析它的工作,设置 Python 的 cookie 库要容易得多。
您能否提供指向您的实现的链接,或答案正文中的代码本身?
这是一个实际实现的 related SO answer
J
Joe Shaw

我最初的计划是让面向公众的 URL 类似于 http://www.example.com/admin/myapp 代理到 http://myapp.internal.example.com/。沿着这条路走下去会导致疯狂。

大多数 webapp,尤其是自托管的,都假设它们将在 HTTP 服务器的根目录下运行,并执行诸如通过绝对路径引用其他文件之类的事情。要解决这个问题,您必须在各处重写 URL:位置标头和 HTML、JavaScript 和 CSS 文件。

我做了write a Flask proxy blueprint,它做到了这一点,虽然它对于我真正想要代理的一个 web 应用程序来说足够好,但它是不可持续的。这是一大堆正则表达式。

最后,我在 nginx 中设置了一个新的虚拟主机,并使用了它自己的代理。由于两者都位于主机的根目录,因此几乎不需要重写 URL。 (几乎不需要什么,处理的是 nginx 的代理模块。)被代理的 webapp 进行自己的身份验证,这对于现在来说已经足够好了。


“我设置了一个新的虚拟主机”的一些插图会很好。
绝对是你的最后一段。 Flask 的优势不是作为一个代理,所以如果可能的话,最好避免作为一个代理使用。我能想到的唯一有效原因是某些应用程序逻辑(例如身份验证或授权)是必需的,而其他应用程序不支持。