Необходимо выполнить функцию после возвращения ответа в Flask
Только для одного запроса мне нужно выполнить функцию после отправки ответа клиенту. Поскольку функция требует времени и заканчивается таймаутом соединения Socket error: [Errno 32] Broken pipe
Есть ли способ в Flask выполнить функцию после возврата запроса
Ответы
Ответ 1
Вы можете попробовать использовать потоковое вещание. См. Следующий пример:
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/.
Ответ 2
Вы можете использовать декоратор after_request или настроить для создания after_this_request, который работает только для этого конкретного запроса.
Взгляните на этот фрагмент http://flask.pocoo.org/snippets/53/
Ответ 3
Для этого не существует Flask-родного способа. after_request
все равно будет выполняться перед возвратом ответа клиенту, а не после него.
Здесь обсуждается проблема и некоторые решения.
Ответ 4
Более общее решение, чем решение итератора колбы, заключается в написании промежуточного программного обеспечения 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
Этот ответ обобщен из моих ответов здесь и здесь.
Ответ 5
Я выложу свое решение.
Вы можете использовать потоки, чтобы вычислить что-либо после того, как вернули что-то в своей функции, вызванной флеш-маршрутом.
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