flask 流式响应 RuntimeError: working outside

2019独角兽企业重金招聘工程师标准>>>
1、问题
最近要实现这样一个功能:某个 cgi 处理会很耗时,需要把处理的结果实时的反馈给前端 , 而不能等到后台全完成了再咔一下全扔前端 , 那样的用户体验谁都没法接受 。
web 框架选的 flask , 这个比较轻量级,看了下官方文档,恰好有个叫 from的功能:
#-from-
可以满足需求,它以 为基?。魇降姆祷厥莸角岸?。看了下官方的例子貌似很简单 , 一笔带过,我又搜了下 ,上面有个老外给了个更加详尽的例子: data withand Flask
文中的答案没有前后端的数据交互过程,那我就根据自己的需求加个 http 的交互过程了:
@app.route('/username', methods=['GET', 'POST'])def index():req =requestprint reqprint "111------------"+ req.method + "\n"def ggg1(req):print req# the req not my pass into the req....print "444------------" + req.method + "\n"if req.method == 'POST':if request.form['username']:urlList = request.form['username'].splitlines()i = 0for url in urlList():i += 1resultStr = urlprint i, resultStryield i, resultStrprint reqprint "222------------" + req.method + "\n"return Response(stream_template('index.html', data=http://www.kingceram.com/post/ggg1(req)))
好吧 , 这么一加,噩梦就开始了 。。。奇葩的问题出现了:
要么第 5 行和第 8 行不等,要么就是第 9 行报错:
if . == 'POST': # :of
继续在 上搜索 , 发现有人遇到了同样的问题,得到的建议是在调用前声明一个上下文:
with app.test_request_context('/username', method='GET'):index()
折腾了老半天 , 还是依旧报错::of
看起来似乎是在进入迭代器以前,原本的的生命周期就已经结束了,因此就没办法再调用了 。
那么要解决就有 2 种办法了:

flask 流式响应 RuntimeError: working outside

文章插图
(1)在进入前将请求复制一份保存下来以供调用 。
(2)利用app. 创建的是一个全新的,将数据传给使用 。
以上这两种办法都曾试过,但是由于理解上的偏差,导致一直未能成功 。后来经过 坚实同学的指点,才明白个中缘由,问题得以解决 。
2、解决方案(1)复制
将请求复制下来但不能直接 req =这种形式,这只是给取了个别名,它们是共享引用 。正确的代码如下:
from flask.ctx import _request_ctx_stackglobal new_request@app.route('/')@app.route('/demo', methods=['POST'])def index():ctx = _request_ctx_stack.top.copy()new_request = ctx.requestdef generateFunc():if new_request.method == 'POST':if new_request.form['digitValue']:num = int(new_request.form['digitValue'])i = 0for n in xrange(num):i += 1print "%s:\t%s" % (i, n)yield i, nreturn Response(stream_template('index.html', data=http://www.kingceram.com/post/generateFunc()))
PS: 其实像这种以下划线开头的变量属于私有变量,外部是不应该调用的,不过坚实同学暂时也没有找到其他能正式调用到它的方法  , 就先这么用着吧 。
(2)构造全新
上面的这种写法:with app.('/', ='GET'):
之所以不可以是因为 app. 创建的是一个全新的 ,它包含的 url, , , form 值都是要在创建时自定义的,它不会把原来的里的数据带进来 , 需要自己传进去,类似这样:
with app.test_request_context('/demo', method='POST', data=http://www.kingceram.com/post/request.form) as new_context:def generateFunc():
PS:应该是做单元测试用的,用来模仿用户发起的 HTTP 请求 。
它做的事,和你通过浏览器提交一个表单或访问某个网页是差不多的 。
例如你传给它 url='xxx'、='post' 等等参数就是告诉它:向 xxx 发起一个 http 请求
(3)关于@
这是官方宣称在 1.0 中实现的一个新特性,#flask. 看说明应该可以更加优雅的解决上述问题 , 
但是试了下貌似不行,可能是组件间的兼容性问题 。
flask 流式响应 RuntimeError: working outside

文章插图
(4)关于 with
New in0.9.
Note that when youdata, theisgone thethe. Flask 0.9you with athat can keep thetheof the :
from flask import stream_with_context, request, Response@app.route('/stream')def streamed_response():def generate():yield 'Hello 'yield request.args['name']yield '!'return Response(stream_with_context(generate()))
the ()you would get aat that point.
REF:
#
3、结论
(1)flask. 和兼容性不是很好,应该尽量不在里调用 ,
把需要的值提前准备好,然后再传到里 。这里也有人遇到同样的问题:
#
用没有效果应该也是上面这个原因 。
(2)在文档语焉不详,同时不到答案的时候,读源码或许是最后的选择,这也是一种能力吧 。。。- _ -
4、Refer:
附坚实同学的与 sf 地址:
5、最后附上完整的测试源码:
# -*- coding: utf-8 -*-import sysreload(sys)sys.setdefaultencoding('utf-8')from flask import Flask, request, Responseapp = Flask(__name__)def stream_template(template_name, **context):# http://flask.pocoo.org/docs/patterns/streaming/#streaming-from-templatesapp.update_template_context(context)t = app.jinja_env.get_template(template_name)rv = t.stream(context)# uncomment if you don't need immediate reaction##rv.enable_buffering(5)return rv@app.route('/')@app.route('/demo', methods=['POST'])def index():with app.test_request_context('/demo', method='POST', data=http://www.kingceram.com/post/request.form) as new_context:def generateFunc():new_request = new_context.requestif new_request.method == 'POST':if new_request.form['digitValue']:num = int(new_request.form['digitValue'])i = 0for n in xrange(num):i += 1print"%s:\t%s" % (i, n)yield i, nreturn Response(stream_template('index.html', data=http://www.kingceram.com/post/generateFunc()))if __name__ =="__main__":app.run(host='localhost', port=8888, debug=True)
【flask 流式响应 RuntimeError: working outside】Bootstrap 101 Template#data {border: 1px solid blue;height: 500px;width: 500px;overflow: hidden;}
nothing received yet{% for i, resultStr in data: %}{% endfor %}