【问题标题】:Need to execute a function after returning the response in FlaskFlask中返回响应后需要执行一个函数
【发布时间】:2013-08-06 14:15:05
【问题描述】:

仅对于一个请求,我需要在向客户端发送响应后执行一项功能。因为该函数需要时间并且最终导致连接超时Socket error: [Errno 32] Broken pipe

Flask中有没有办法在返回请求后执行函数

【问题讨论】:

  • 您希望函数在请求完成后做什么?如果它写入日志,那是一回事,但如果你想将数据返回给用户,如果连接确实超时,就没有办法做到这一点。这不是 Flask 的东西,这只是 HTTP 的东西;浏览器、Web 服务器或介于两者之间的任何东西都可以关闭连接。如果您的请求花费的时间太长,请考虑让它在单独的进程中运行,第一个请求启动它,您的应用会自动(或用户手动)通过其他请求定期检查任务的状态。

标签: python flask


【解决方案1】:

我将公开我的解决方案。

在烧瓶路由调用的函数中返回某些内容后,您可以使用线程来计算任何内容。

import time
from threading import Thread
from flask import request, Flask
app = Flask(__name__)


class Compute(Thread):
    def __init__(self, request):
        Thread.__init__(self)
        self.request = request

    def run(self):
        print("start")
        time.sleep(5)
        print(self.request)
        print("done")


@app.route('/myfunc', methods=["GET", "POST"])
def myfunc():
        thread_a = Compute(request.__copy__())
        thread_a.start()
        return "Processing in background", 200

【讨论】:

  • 对此解决方案的 +1:线程在任何类型的响应中都能很好地工作,例如您想要返回 redirect 而不是 Response 对象。
  • 这就是我需要返回 200 并异步执行其他操作。
  • 非常简洁。这就是我最终使用的。
【解决方案2】:

您可以尝试使用流式传输。见下一个例子:

import time
from flask import Flask, Response

app = Flask(__name__)

@app.route('/')
def main():
    return '''<div>start</div>
    <script>
        var xhr = new XMLHttpRequest();
        xhr.open('GET', '/test', true);
        xhr.onreadystatechange = function(e) {
            var div = document.createElement('div');
            div.innerHTML = '' + this.readyState + ':' + this.responseText;
            document.body.appendChild(div);
        };
        xhr.send();
    </script>
    '''

@app.route('/test')
def test():
    def generate():
        app.logger.info('request started')
        for i in range(5):
            time.sleep(1)
            yield str(i)
        app.logger.info('request finished')
        yield ''
    return Response(generate(), mimetype='text/plain')

if __name__ == '__main__':
    app.run('0.0.0.0', 8080, True)

此示例中的所有魔法都在生成器中,您可以在其中启动响应数据,然后执行一些人员并产生空数据以结束您的流。

详情请查看http://flask.pocoo.org/docs/patterns/streaming/

【讨论】:

  • 这真是太聪明了。有了这个,用户甚至不必停留在网页上就可以继续在后台处理,因为浏览器已经结束了连接。该程序正在连接到它自己。真是聪明的东西。竖起两个大拇指!
【解决方案3】:

比烧瓶迭代器解决方案更通用的解决方案是编写一个WSGI中间件,为response close方法添加回调。在这里,我们使用 werkzeug ClosingIterator 助手和一个烧瓶应用扩展来实现这一点:

import traceback
from werkzeug.wsgi import ClosingIterator

class AfterResponse:
    def __init__(self, app=None):
        self.callbacks = []
        if app:
            self.init_app(app)

    def __call__(self, callback):
        self.callbacks.append(callback)
        return callback

    def init_app(self, app):
        # install extension
        app.after_response = self

        # install middleware
        app.wsgi_app = AfterResponseMiddleware(app.wsgi_app, self)

    def flush(self):
        for fn in self.callbacks:
            try:
                fn()
            except Exception:
                traceback.print_exc()

class AfterResponseMiddleware:
    def __init__(self, application, after_response_ext):
        self.application = application
        self.after_response_ext = after_response_ext

    def __call__(self, environ, after_response):
        iterator = self.application(environ, after_response)
        try:
            return ClosingIterator(iterator, [self.after_response_ext.flush])
        except Exception:
            traceback.print_exc()
            return iterator

然后您可以像这样使用您的 after_response 装饰器:

import flask
import time
app = flask.Flask("after_response")
AfterResponse(app)

@app.after_response
def after():
    time.sleep(2)
    print("after_response")

@app.route("/")
def home():
    return "Success!\n"

当你 curl 时,你会看到它立即响应并且 curl 关闭,然后 2 秒后你的“之后”消息出现在日志中:

127.0.0.1 - - [25/Jun/2018 15:41:51] "GET / HTTP/1.1" 200 -
after_response

这个答案是从我的回答herehere总结出来的。

【讨论】:

  • 回调函数将在没有任何上下文的情况下运行,因为此时请求上下文和应用程序上下文都已被拆除。通过在拆卸之前创建一个闭包并将其用于回调,我找到了一个可用的解决方案。在这里回答:stackoverflow.com/a/63080968/78903
【解决方案4】:

没有 Flask 原生的方式来实现这一点。 after_request 仍将运行将响应返回给客户端,而不是之后。

Here is a discussion of the problem and some solutions.

【讨论】:

    【解决方案5】:

    您可以使用 after_request 装饰器或自定义创建仅适用于该特定请求的 after_this_request。

    看看这个sn-p http://flask.pocoo.org/snippets/53/

    【讨论】:

    • 感谢 yash 的快速回复! after_this_request 也等待定义的函数完成然后返回响应有没有办法先发送响应然后执行函数
    • 有没有办法在flask中设置请求连接超时或响应连接超时?
    【解决方案6】:

    您可以通过组合 after_this_request 和 response.call_on_close 来推迟具有有限上下文的路由特定操作。请注意,请求和响应上下文将不可用,但路由函数上下文仍然可用。因此,您需要将所需的任何请求/响应数据复制到本地变量中以进行延迟访问。

    @app.route('/')
    def index():
        # Do your pre-response work here
        msg = 'Hello World!'
        @flask.after_this_request
        def add_close_action(response):
            @response.call_on_close
            def process_after_request():
                # Do your post-response work here
                time.sleep(3.0)
                print('Delayed: ' + msg)
            return response
        return msg
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-27
      • 1970-01-01
      • 1970-01-01
      • 2018-06-09
      • 1970-01-01
      • 2017-02-16
      • 1970-01-01
      • 2021-04-10
      相关资源
      最近更新 更多