【问题标题】:Flask - response to a POST - confusing behaviourFlask - 对 POST 的响应 - 令人困惑的行为
【发布时间】:2018-05-02 12:50:07
【问题描述】:

我对 Flask 中的以下行为完全感到困惑。我确信发生了一些基本问题,但我不知道这是脚本问题还是服务器问题,所以我发布了我能想到的最短示例。

页面有两种方式将数据发布到服务器。一个由提交<form> 触发,另一个由侦听Ctrl+V 的脚本触发。我相信两者都应该导致最后一次render_template 通话。 但是只有第一个可以。

paste.html

{%if msg %}
    {{ msg }}
{% else %}
Waiting
{% endif %}    
<form action="/pastetest" method="post">
      Dummy<br>
  <input type="text" name="foo" value="bar">
  <br>
  <input type="submit" value="Send info" />
</form>

脚本 侦听“粘贴”事件 (Ctrl V) 并在表单中发布一些虚拟字符串。

document.addEventListener('paste', function (e) {sendform(e);},false);
sendform = function (e) {
        var fd = new FormData();
        fd.append('data','testing paste');
        var request = new XMLHttpRequest();
        request.open("POST", "/pastetest");
        request.send(fd);
        console.log("Sent")
    };

Flask 蟒蛇:

@app.route('/')
def index():
    return render_template('paste.html',msg="Waiting")

@app.route('/pastetest',methods=['POST']) 
def image_upload():
    if request.method == 'POST':
        print("On the server")
        print(request.form)
        alldone="All done time=%d" % time.time()
        print(alldone)
        return redirect(url_for('confirm',msg=alldone))

@app.route('/confirm/<msg>')
def confirm(msg):
    print("At confirm",msg)
    return render_template("paste.html",msg=msg) #<---should end up here

从控制台我看到正在发布数据并且正在调用confirm127.0.0.1 - - [19/Nov/2017 00:51:42] "GET /confirm/All%20done%20time%3D1511049102 HTTP/1.1" 200 -

但页面仅在提交按钮之后呈现。否则会卡在Waiting

编辑: 澄清一下:我需要的是获取发布的数据(只是上面的一个虚拟字符串,但将是剪贴板中的一个 blob),用它做一些事情,然后用结果渲染一个模板。最简单的方法是什么?

(我对 js/client-server 等一无所知。只是想为我已经编写的图像分析程序制作一个简单的 Web 界面。感谢简单的解释 :)

【问题讨论】:

  • 您的模板缺少基本的 HTML 结构。

标签: javascript python flask


【解决方案1】:

它不会呈现您的页面,因为您只是发送XmlHttpRequest,仅此一项不会让您的浏览器呈现响应。

您的 JavaScript 确实向您从日志中看到的指定 URL 发送了一个 POST 请求,但是由于您没有对 the response to that request 执行任何操作,因此没有任何反应。

这会起作用...

<form action="/pastetest" method="post">

...因为提交您的 HTML 表单,您的浏览器不仅会向action 中的 URL 发送 POST 请求,它还会更改该 URL 的位置并呈现它得到的响应。

您会注意到,在提交 HTML 表单时,浏览器地址栏中的 URL 会发生变化,而在发送 XmlHttpRequest 时不会发生变化。

编辑,来自 cmets 的总结:

在这两种情况下,服务端都会执行image_uploadconfirm这两个函数,也就是说从服务端的角度来看,它们是等价的,这里的主要区别在于使用XmlHttpRequest方法时,响应不会自动呈现通过浏览器。

至于问题中描述的目标,让浏览器在两种情况下都呈现结果页面,一旦我们得到响应,我们就必须考虑在XmlHttpRequest 情况下发生了什么:

  • 服务器已经执行了函数image_uploadconfirm
  • 来自confirm 函数的渲染模板在我们的响应中

如果我们不介意 confirm 函数被执行两次,我们可以简单地将浏览器的 window.location 指向请求的 the responseURL

当我们这样做时,该函数肯定会执行两次,它已经执行了一次以生成我们得到的响应,并让我们的浏览器转到 responseURL(在我们的例子中,这将是confirm函数对应的URL),它会再次执行。

如果我们想防止这种情况发生,我们需要另一种解决方案。

一种选择是通知服务器我们在进行初始上传时不希望重定向到呈现的响应页面,而只需要状态信息(我们可以在这里使用 HTTP 状态代码,例如我们解释 status= 200 表示成功),并且服务器响应应该是我们需要指向浏览器的window.location 的位置。

这样,服务器将只执行一次confirm,因为它不会在我们的 XmlHttpRequest 的情况下自动重定向我们,相反它只会告诉我们需要去哪里,并离开实际去那里的责任客户端 JavaScript。

这意味着改变我们的服务器端代码,服务器需要辨别我们想要什么类型的响应。我们可以使用两个不同的端点来实现这一点,例如/pastetest 用于表单提交,/pastetestxhr 用于 XmlHttpRequest。

另一种选择是使用 JavaScript 从响应中提取部分呈现的模板,并替换我们当前页面的部分,例如从页面中删除我们的 HTML 表单,并将其替换为响应中的成功通知。

为了方便地执行此操作,我们可能希望使用一些客户端 JavaScript 库,例如 jQuery

这也只会执行一次confirm,因为我们会在收到响应后避免将浏览器位置更改为该端点,而是使用我们已有的响应。

这种方法不需要更改任何服务器端代码,但会在我们的客户端代码中引入更多复杂性。

第三种选择可能是一种混合方法,我们通知服务器我们不想要自动重定向,并期望作为响应的 HTML sn-p 我们可以按原样插入到我们的页面中,而无需任何复杂的解析响应提取我们想要插入页面的点点滴滴。

这需要稍微复杂一点的客户端 JavaScript(虽然不如我们上面列出的选项 2 复杂)和服务器端代码更改。

【讨论】:

  • 让我看看我是否明白这一点:最终的render_template 将作为对XmlHttpRequest 的回应回来?那么我如何“关闭”请求(不知道正确的术语),以便服务器可以继续渲染模板。
  • @A.Roy 没错,响应正文将包含您呈现的 HTML(您可以在浏览器的开发者控制台中看到)。
  • @A.Roy 就服务器而言,此时请求/响应已经完成,它不关心浏览器中的 JavaScript 对响应做什么或不做什么.您必须处理在 JavaScript 中显示结果,如获取响应正文,例如在 alert() 中显示内容,请参阅 link in my answer
  • 我看到了链接,但很难理解。我添加了一点编辑来解释我正在尝试做的事情。那么我应该从服务器返回msg 并代替链接中的警报,通过window.location.href = ... 重定向url 吗?
  • @A.Roy 如果您对客户端 JavaScript 特技还不满意,我建议您考虑使用file uploads with flask,这种方法往往要担心的“移动部件”要少得多,并且从您已经工作的 HTML 表单提交中应该非常简单。
【解决方案2】:

浏览器不会渲染来自 XmlHttpRequest 的响应,除非你通过 JS 告诉浏览器这样做。我建议您查看浏览器的“开发工具”并在“网络”选项卡下进行检查。您将在那里看到 XHR 的响应,但它不会替换内容或浏览器。

【讨论】:

    猜你喜欢
    • 2011-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多