如何在服务器端的 Flask 中完成服务器发送事件时重定向?

How to redirect when server-sent-event is finished in Flask on server side?

问题

当 server-sent-event 在 Flask 和服务器端完成时如何重定向到另一个页面?

问题描述

在客户端代码 (JavaScript) 上实现了一个 EventSource,在服务器端代码 (Flask, Python) 上返回了一个 Response。如果从服务器发送了最后一个项目,则 EventSource 将关闭。我看不到通过服务器端代码重定向到另一个站点的明确解决方案。也许答案很简单,但我不明白。

类似问题

类似的问题是How to stop Server-Sent Events and one possible answer in the comment How do server-sent events actually work?。我不确定 php 和 python 是否相同,所以我为此提出了一个新问题。我也没听懂

EDIT How do I close a Server-Send Events connection in Flask? 与这个问题密切相关。那里的问题主要是看看如何从 Flask 中停止 SSE。

客户端的工作解决方案

所以我想出了一个可行的解决方案(进度条),但仅限于客户端。我必须如何更改代码才能获得通过 Flask 函数重新连接的工作示例?

相关代码片段

HTML/Jinja

{% block additional_stylesheets %}
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="{{ url_for('static', filename='data_import.css') }}" />
{%  endblock %}
{% block additional_javascripts %}
    <script src="{{ url_for('static', filename='js/data_import.js') }}"></script>
{% endblock %}
{% block content %}
    <div class="progress">
        <div class="progress-bar progress-bar-striped active"
             role="progressbar" aria-valuenow="0" aria-valuemin="0"
             aria-valuemax="100">
        </div>
    </div>
{% endblock %}

JavaScript

$(document).ready(function()
    {
        var source = new EventSource("/progress");

        source.addEventListener('import-progress', function(event)
            {
                $('.progress-bar')
                    .css('width', event.data + '%')
                    .attr('aria-valuenow', event.data);
            }, false
        );

        source.addEventListener('last-item', function()
            {
                source.close();
                redirect();
            }, false
        );
    }
);

# This works! But how to do the same thing without calling redirect()
# on client site?
function redirect()
{
    window.document.location.href = window.location.protocol + "//" +
            window.location.host + "/products.html";
}

烧瓶

from foo import app
from flask import render_template, Response

@app.route('/data_import.html')
def data_import():
    return render_template(
        'data_import.html')


@app.route('/progress')
def progress():
    return Response(import_progress(), mimetype='text/event-stream')

def import_progress():
    """
    Just a small example
    """
    for number in range(1, 101):
        sse_id = str(number)
        sse_progress = str(number)
        sse_event = 'import-progress'

        if number == 100:
            sse_event = 'last-item'

        yield "id:{_id}\nevent:{event}\ndata:{progress}\n\n".format(
            _id=sse_id, event=sse_event, progress=sse_progress)

我尝试了很多让重定向工作。但我不知道该怎么做。到目前为止的每一次尝试都失败了。

您要做的是将重定向 URL 作为最后一个事件发送 - 您仍然需要使用 JavaScript 进行重定向,但您不必对路径进行硬编码还有:

def import_progress():
    """
    Just a small example
    """
    for number in range(1, 101):
        sse_id = str(number)
        sse_data = str(number)
        sse_event = 'import-progress'

        if number == 100:
            sse_event = 'last-item'
            sse_data = url_for('product_list')

        yield "id:{_id}\nevent:{event}\ndata:{data}\n\n".format(
            _id=sse_id, event=sse_event, data=sse_data)

然后您的最后一项处理程序变为:

source.addEventListener('last-item', function(event) {
     source.close();
     redirect(event.data);
   }, false
);

并且redirect变得简单:

function redirect(url) {
  document.location = url;
}