Flask 视图装饰器和流式内容

问题描述

Flask 允许用户创建 custom endpoint decoratorsto stream responses(基本上通过在 flask.Response 中返回一个生成器)。

我想让这两个概念一起工作。这是一个装饰器,它在处理请求之前和之后初始化和关闭上下文。 (在现实生活中,它会做数据库连接相关的事情):

def mydecorator(f):
    @wraps(f)
    def decorated_function(*args,**kwargs):
        print("Start context")
        result = f(*args,**kwargs)
        print("End context")
        return result
    return decorated_function

在这里有两个端点,第一个是常规端点,第二个是流式端点。

@app.route("/regular")
@mydecorator
def regular_endpoint():
    print("start normal")
    random_function_that_need_the_decorator_context()
    print("end normal")
    return render_template("whatever.html")
# prints: start context,start normal,end normal,end context

@app.route("/streamed")
@mydecorator
def streamed_endpoint():
    def mygenerator():
        print("start generator")
        random_function_that_need_the_decorator_context()
        for _ in range(1000):
            yield "something"
        print("end generator")

    return Response(mygenerator())
# prints: start context,end context,start generator,end generator

常规端点按预期工作,但流端点失败,因为内部生成函数需要装饰器上下文,但在生成函数执行时装饰器上下文已关闭

有没有办法在生成器执行时保持装饰器上下文打开? 在flask 中有一个stream_with_context 函数,但它似乎只提供flask 请求上下文。使用 after_request 并没有得到更好的结果,因为在执行生成器之前调用了该函数

解决方法

一旦在服务器上启动响应,Flask 将自动删除请求上下文。这主要是为了防止内存泄漏。因此,您必须明确告诉它保留它。你可以用“stream_with_context”来做到这一点:

from flask import stream_with_context

@app.route("/streamed")
@mydecorator
def streamed_endpoint():
    def mygenerator():
       print("start generator")
       random_function_that_need_the_decorator_context()
       for _ in range(1000):
          yield "something"
       print("end generator")

    return Response(stream_with_context(mygenerator()))

Flask==1.1.1 中测试和工作

可以找到更多信息here