flask 流式响应 RuntimeError: working outside of request context

阅读: 评论:0

flask 流式响应 RuntimeError: working outside of request context

flask 流式响应 RuntimeError: working outside of request context

2019独角兽企业重金招聘Python工程师标准>>>

1、问题

最近要实现这样一个功能:某个 cgi 处理会很耗时,需要把处理的结果实时的反馈给前端,而不能等到后台全完成了再咔一下全扔前端,那样的用户体验谁都没法接受。

web 框架选的 flask,这个比较轻量级,看了下官方文档,恰好有个叫 Streaming from Templates 的功能:

可以满足需求,它以 generate yield 为基础,流式的返回数据到前端。看了下官方的例子貌似很简单,一笔带过,我又搜了下 stackoverflow,上面有个老外给了个更加详尽的例子:Streaming data with Python and Flask

文中的答案没有前后端的数据交互过程,那我就根据自己的需求加个 http 的交互过程了:

&#ute('/username', methods=['GET', 'POST'])
def index():req =requestprint reqprint "111------------"  + hod + "n"def ggg1(req):print req  # the req not my pass into the print "444------------" + hod + "n"if hod == 'POST':if request.form['username']:urlList = request.form['username'].splitlines()i = 0for url in urlList():i += 1resultStr = urlprint i, resultStryield i, resultStrprint reqprint "222------------" + hod + "n"return Response(stream_template('index.html', data=ggg1(req)))

好吧,这么一加,噩梦就开始了。。。奇葩的问题出现了:

要么第 5 行和第 8 行不等,要么就是第 9 行报错:

 hod == 'POST':  # RuntimeError: working outside of request context

继续在 stackoverflow 上搜索,发现有人遇到了同样的问题,得到的建议是在调用前声明一个 request 上下文:

with st_request_context('/username', method='GET'):index()

折腾了老半天,还是依旧报错:RuntimeError: working outside of request context

看起来似乎是在进入迭代器以前,原本的 request 的生命周期就已经结束了,因此就没办法再调用了。

那么要解决就有 2 种办法了:

(1)在进入 generationFunc 前将请求复制一份保存下来以供 generationFunc 调用。

(2)利用 st_request_context 创建的是一个全新的 request,将数据传给 generationFunc 使用。

以上这两种办法都曾试过,但是由于理解上的偏差,导致一直未能成功。后来经过 坚实 同学的指点,才明白个中缘由,问题得以解决。

2、解决方案

(1)复制 request

将请求复制下来但不能直接 req = request 这种形式,这只是给 request 取了个别名,它们是共享引用。正确的代码如下:

from  import _request_ctx_stack
global new_request
&#ute('/')
&#ute('/demo', methods=['POST'])
def index():ctx = _request_p.copy()new_request = questdef generateFunc():if hod == '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=generateFunc()))

PS: 其实像 _request_ctx_stack 这种以下划线开头的变量属于私有变量,外部是不应该调用的,不过坚实同学暂时也没有找到其他能正式调用到它的方法 ,就先这么用着吧。

(2)构造全新 request

上面的这种写法:st_request_context('/username', method='GET'):

之所以不可以是因为 st_request_context 创建的是一个全新的 request,它包含的 url, method, headers, form 值都是要在创建时自定义的,它不会把原来的 request 里的数据带进来,需要自己传进去,类似这样:

with st_request_context('/demo', method='POST', data=request.form) as new_context:def generateFunc():

PS: test_request_context 应该是做单元测试用的,用来模仿用户发起的 HTTP 请求。
它做的事,和你通过浏览器提交一个表单或访问某个网页是差不多的。
例如你传给它 url='xxx'、method='post' 等等参数就是告诉它:向 xxx 发起一个 http 请求

(3)关于 @copy_current_request_context

这是官方宣称在 1.0 中实现的一个新特性,.copy_current_request_context 看说明应该可以更加优雅的解决上述问题,

但是试了下貌似不行,可能是组件间的兼容性问题。

(4)关于 Streaming with Context

New in version 0.9.
Note that when you stream data, the request context is already gone the moment the function executes. Flask 0.9 provides you with a helper that can keep the request context around during the execution of the generator:

from flask import stream_with_context, request, Response&#ute('/stream')
def streamed_response():def generate():yield 'Hello 'yield request.args['name']yield '!'return Response(stream_with_context(generate()))

Without the stream_with_context() function you would get a RuntimeError at that point.

REF:

=1#20189866

3、结论

(1&#quest 和 streaming templates 兼容性不是很好,应该尽量不在 streaming templates 里调用 request,
把需要的值提前准备好,然后再传到 templates 里。这里也有人遇到同样的问题:

用 copy_current_request_context 没有效果应该也是上面这个原因。

(2)在文档语焉不详,同时 google 不到答案的时候,读源码或许是最后的选择,这也是一种能力吧。。。 - _ -

4、Refer:


/


/

附坚实同学的 github 与 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):# .update_template_context(context)t = app._template(template_name)rv = t.stream(context)# uncomment if you don't need immediate reaction##rv.enable_buffering(5)return rv&#ute('/')
&#ute('/demo', methods=['POST'])
def index():with st_request_context('/demo', method='POST', data=request.form) as new_context:def generateFunc():new_request = questif hod == '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=generateFunc()))if __name__ == "__main__":app.run(host='localhost', port=8888, debug=True)

 

<!DOCTYPE html>
<html>
<head><title>Bootstrap 101 Template</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- Bootstrap --><!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --><!-- WARNING: Respond.js doesn't work if you view the page via file:// --><!--[if lt IE 9]><script src=".7.0/html5shiv.js"></script><script src=".js/1.3.0/respond.min.js"></script><![endif]-->
</head>
<body><style>#data {border: 1px solid blue;height: 500px;width: 500px;overflow: hidden;}
</style>
<script src=".js"></script><script>function tailScroll() {var height = $("#data").get(0).scrollHeight;$("#data").animate({scrollTop: height}, 5);}
</script><form role="form" action="/demo" method="POST"><textarea class="form-control" rows="1" name="digitValue"></textarea><button type="submit" class="btn btn-default">Submit</button>
</form><div id="data" style="position:relative;height:400px; overflow-x:auto;overflow-y:auto">nothing received yet</div>{% for i, resultStr in data: %}<script>$("<div />").text("{{ i }}:t{{ resultStr }}").appendTo("#data")tailScroll();</script>
{% endfor %}<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src=".js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="/static/dist/js/bootstrap.min.js"></script>
</body>
</html>

6、推荐阅读:

[1] 用Flask实现视频数据流传输

/

[2] Video Streaming with Flask

[3] Flask 的 Context 机制

/

[4] flask 源码解析:session

/

转载于:

本文发布于:2024-02-01 19:50:04,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170678820439042.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:流式   RuntimeError   flask   context   request
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23