Python 使用Flask代理到另一个web服务

Python 使用Flask代理到另一个web服务,python,proxy,nginx,flask,httplib,Python,Proxy,Nginx,Flask,Httplib,我想将对Flask应用程序的请求代理到计算机上本地运行的另一个web服务。为此,我宁愿使用Flask,也不愿使用更高级别的nginx实例,这样我们就可以重用应用程序中内置的现有身份验证系统。我们越能保持这种“单一登录”越好 是否有现有的模块或其他代码来执行此操作?尝试将Flask应用程序连接到httplib或urllib之类的东西被证明是一件痛苦的事情。我在基于Werkzeug的应用程序中使用httplib实现了一个代理(就像你的情况一样,我需要使用webapp的身份验证和授权) 尽管Flask

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


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

我在基于Werkzeug的应用程序中使用httplib实现了一个代理(就像你的情况一样,我需要使用webapp的身份验证和授权)

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


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

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

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

我做到了这一点,虽然它对于我真正想代理的一个网络应用来说已经足够好了,但它是不可持续的。这是一大堆正则表达式


最后,我在nginx中设置了一个新的虚拟主机,并使用了它自己的代理。因为两者都位于主机的根目录,所以URL重写基本上是不必要的。(需要做的很少,由nginx的代理模块处理。)被代理的Web应用程序自己进行身份验证,目前已经足够好了。

我花了很多时间做同样的事情,最终找到了一个使用请求库的解决方案,似乎效果很好。它甚至可以在一个响应中设置多个cookie,这需要一些调查才能弄清楚。下面是flask view函数:

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月更新:
excluded_headers
可能应该包括由定义的所有“逐跳头”。

在为IE7等不支持跨域安全的旧浏览器提供AJAX服务时,这个问题也很重要。httplib有什么具体问题?@jd:鉴于flask位于WSGI的应用程序端,我不确定我是否能有效地转发所有数据。例如,Flask请求对象似乎不包含我希望传递到httplib的原始请求(甚至请求头)。这并不是说这是不可能的,这只是一个痛苦,我希望现有的模块已经做到了。谢谢,我今天下午黑客了一些东西。但是,由于httplib不能很好地处理cookies,所以cookies有各种各样的问题。不幸的是,我想我需要修改响应来做一些简单的URL重写(也就是说,在我的例子中,只有一个cookie需要捕获,所以正则表达式负责解析它,设置Python的cookie库要容易得多。你能在答案的主体中提供一个到你的实现的链接,或者代码本身吗?下面是一个关于实际实现的示例。“我设置了一个新的虚拟主机”的一些说明很好。肯定是你的最后一段。Flask的优势不是作为代理,所以如果可能的话,最好不要作为代理使用。我能想到的唯一有效的原因是某些应用程序逻辑,如身份验证或授权是必需的,而另一个应用程序不支持。@Evan nice解决方案。它不处理3xx redir但是,由于重定向url可能指向代理主机,有人可以添加您在MWE应用程序中如何称呼它吗?这太棒了,非常感谢!(这是让ngrok同时处理前端和后端所需要的。)但是,对我来说,
request.host\u url
包括
http://
和一个尾随斜杠,所以我的替换行是:
request.url.replace(request.host\u url,'http://new-domain.com/“)
@Ire我遇到了这个问题,并添加了一个要修复的编辑。我所做的只是用
headers=[(名称,值)if(name.lower()!=“location”)else(名称,值.replace('http://new-domain.com/“,request.host_url)),如果name.lower()不在排除的_头中,则在resp.raw.headers.items()中为(name,value)
。这只是修复了位置头中的url。(感谢@jbasko指出尾部斜杠的问题)您还可以流式传输响应内容,而不是完全在服务器上读取。为此,将上面的
resp.content
替换为
resp.iter\u内容(chunk\u size=10*1024)
并将
content\u type=r.headers['content-type']
参数添加到
响应
构造函数中。