在Flask中返回响应后需要执行一个函数

nag*_*4ce 29 python flask

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

Flask中有一种方法 可以在返回请求后执行函数

tbi*_*icr 12

您可以尝试使用流媒体.见下一个例子:

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)
Run Code Online (Sandbox Code Playgroud)

这个示例中的所有魔法都在genarator中,您可以在执行一些人员并产生空数据以结束流之后启动响应数据.

对于ditails,请查看http://flask.pocoo.org/docs/patterns/streaming/.


Mat*_*ory 9

比烧瓶迭代器解决方案更通用的解决方案是编写WSGI中间件,该中间件在响应关闭方法中添加回调。在这里,我们使用werkzeug ClosingIterator帮助器和flask应用程序扩展来实现此目的:

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
Run Code Online (Sandbox Code Playgroud)

然后,您可以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"
Run Code Online (Sandbox Code Playgroud)

卷曲时,您会看到它立即响应并卷曲关闭,然后2秒钟后,“ after”消息将出现在日志中:

127.0.0.1 - - [25/Jun/2018 15:41:51] "GET / HTTP/1.1" 200 -
after_response
Run Code Online (Sandbox Code Playgroud)

这个答案是从我这里这里的答案中总结出来的。


Bra*_*ang 7

没有Flask本地方法来实现这一目标.将响应返回给客户端之前after_request仍然会运行,而不是之后.

以下是对问题和一些解决方案的讨论.


Ale*_*kos 7

我将公开我的解决方案。

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

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
Run Code Online (Sandbox Code Playgroud)


cod*_*eek 5

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

看一下这个片段http://flask.pocoo.org/snippets/53/

  • 谢谢你快速回复!after_this_request还等待定义的函数完成后再返回响应是否有任何方法先发送响应然后执行函数 (11认同)