Необходимо выполнить функцию после возврата ответа в Flask

Для одного запроса мне нужно выполнить функцию после отправки ответа клиенту. Потому что функция занимает время, и это заканчивается в таймауте соединения Socket error: [Errno 32] Broken pipe

Есть ли способ во Flask выполнить функцию после возврата запроса

6 ответов

Я выставлю свое решение.

Вы можете использовать потоки для вычисления чего угодно после того, как что-то вернуло в вашу функцию, вызванную маршрутом фляги

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

Вы можете попробовать использовать потоковую передачу. Смотрите следующий пример:

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)

Вся магия в этом примере в genarator, где вы можете запустить ответные данные, после чего сделать некоторые кадры и получить пустые данные для завершения вашего потока.

Подробные сведения см. По адресу http://flask.pocoo.org/docs/patterns/streaming/.

Более общее решение, чем решение итератора фляги, состоит в том, чтобы написать промежуточное программное обеспечение WSGI, которое добавляет обратный вызов к методу закрытия ответа. Здесь мы используем 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"

Когда вы свернете это, вы увидите, что оно отвечает немедленно и закручивается, а через 2 секунды в журналах появляется ваше сообщение "после":

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

Этот ответ обобщен из моих ответов здесь и здесь.

Нет никакого родного способа сделать это. after_request будет по-прежнему выполняться до возвращения ответа клиенту, а не после.

Вот обсуждение проблемы и некоторые решения.

Вы можете использовать декоратор after_request или настроить для создания after_this_request, который работает только для этого конкретного запроса.

Посмотрите на этот фрагмент http://flask.pocoo.org/snippets/53/

Вы можете отложить действия по конкретному маршруту с ограниченным контекстом, объединив 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
Другие вопросы по тегам