【问题标题】:Form that makes browser redirect when accessed by either a regular form submit or an Ajax request - is this possible?当通过常规表单提交或 Ajax 请求访问时使浏览器重定向的表单 - 这可能吗?
【发布时间】:2009-09-22 22:15:00
【问题描述】:

我有一个带有表单的网页。当用户提交表单时,我希望服务器使浏览器重定向到与表单操作不同的页面。现在,我正在通过使用 PHP 的 header 函数发送 302 状态代码来执行此操作。它工作正常。

我试图使服务器上的页面以相同的方式重定向浏览器,无论它是正常提交​​(没有 Javascript)还是通过 Ajax。我试图通过将窗口位置设置为 Location 标头中的任何 URL 来做到这一点。我正在使用 jQuery,并进行如下调用:

$.ajax({
    url: this.action,
    type: "POST",
    data: getFormData(this),
    complete: function(request) {
        window.location.assign(request.getResponseHeader("Location"));
    }
});

然而,这并没有奏效。想了想,才发现这并不奇怪。在 Ajax 请求中,浏览器应该在更改 readyState 之前透明地处理重定向响应,例如 302 代码。当完整的函数运行时,它正在寻找最终目的地的 Location 标头,但没有找到。

作为实验,然后我尝试发送带有 Location 标头的 200 状态代码。我尝试了 Ajax 请求,它运行良好。但是,当我进行非 Ajax 提交时,它不起作用。浏览器转到表单操作页面并停留在那里,就像它忽略了 Location 标头一样。

有没有办法在这两种情况下重定向相同的页面,而服务器不必知道或关心请求是否是 Ajax 请求?

如果这很重要,我在各种浏览器(IE8、IE7、IE6、Firefox 3.5、Chrome)中尝试了表单,每次都得到类似的结果。另外,我正在做一个发布请求以避免碰到 IE 的 2083 个字符的 URL 长度限制。

【问题讨论】:

    标签: php javascript jquery html ajax


    【解决方案1】:

    HTTP 302 response are consumed silently by XmlHttpRequest implementations(例如 jQuery 的 ajax 函数)。这是一个功能。

    我过去解决此问题的方法是检测 XmlHttpRequests 并发出 "Content-Location" header(而不是 "Location" 标头)。执行此操作的最跨库方法是检查服务器端代码中的 "X-Requested-With" http 标头(jQuery、Prototype、Mootools 等都设置了这个):

    if (@$_SERVER['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest') {
        header('Content-Location: ' . $redirect_url);
    } else {
        header('Location: ' . $redirect_url);
    }
    

    您仍然需要对客户端代码进行特殊处理:

    $.ajax({
        // ...
        complete: function(xhr) {
            var redirect_url = xhr.getResponseHeader("Content-Location");
            if (redirect_url) {
                window.location = redirect_url;
            }
        }
    })
    

    【讨论】:

      【解决方案2】:

      如果用户无论如何都被重定向,为什么要使用 Ajax?像这样的 Ajax 的全部意义在于在不刷新页面的情况下更改页面,因此您使用的技术有点像构建一个输出到碎纸机漏斗的打印机。

      【讨论】:

      • 也许如果有错误没有重定向例如验证类型的东西
      • 好问题。服务器上的相同脚本被不同页面上的许多表单使用,其中一些设计得不好并尝试使用 Ajax 提交,但没有任何充分的理由这样做。在我修复这些表单之前,这可能是一个临时解决方法。此外,我很好奇这是否可以做到,因为它可能有一天会派上用场。我可能想让我的网站使用 Ajax 来做一些花里胡哨的东西,但仍然可以为没有 Javascript 支持的客户工作。另外,我可能需要发出一个 Ajax 请求,并且要么停留在同一页面上,要么重定向,具体取决于服务器的决定。
      【解决方案3】:

      我想更多地了解您的用例。据我了解,您试图让您的应用程序根据其中的 Ajax 调用的“位置”标头加载页面?我想问为什么?

      HTTP 标头似乎不是从中获取该信息的正确位置。您的应用程序本质上不是在发出“我应该重定向到哪里?”的查询。没有理由 Ajax 响应实际上必须使用 302 和“位置”标头进行响应。为什么不让它响应包含新 URL 的 JSON 或 XML?

      编辑:只需重新阅读倒数第二段即可。我不确定是否有实现您想要的好方法。这个概念对我来说听起来很糟糕。 :)

      【讨论】:

        【解决方案4】:

        将附加参数传递给您的 ajax 请求,以便轻松识别请求类型。当 ajax - 不重定向时 - 只发送目标 url,然后通过 location.href 在 ajax 回调中重定向客户端

        像这样:

        $.post('/controller/action', {formdata}, function (redirect_to) { location.href = 重定向到; });

        【讨论】:

          【解决方案5】:

          将“完成”工作:

          $.ajax({
              type: frm.attr('method'),
              url: frm.attr('action'),
              data: frm.serialize(),
              complete: complete(xhr, status) {
                 window.location.assign(xhr.getResponseHeader("Location"));
          
              }
          });
          

          【讨论】:

          • 它适用于 200 响应,但不适用于 302 响应。阅读我的帖子了解更多详情。
          【解决方案6】:

          您是否尝试使用错误函数而不是完整函数?

          $.ajax({
              url: this.action,
              type: "POST",
              data: getFormData(this),
              error: function(request) {
                  if(request.status == 302)
                      window.location.assign(request.getResponseHeader("Location"));
              }
          });
          

          jQuery 向错误函数发送任何 3xx 响应。也许“位置”标题在这个阶段仍然可用。

          【讨论】:

          • 我尝试了类似的方法,但没有成功。它不应该那样工作。浏览器应该在有机会运行完整、成功或错误函数之前透明地遵循 302 代码。有关详细信息,请参阅我之前的评论 (stackoverflow.com/questions/373087/…)。
          • 我明白了。看起来你需要用不同的响应来回复 ajax。好痛
          【解决方案7】:

          为什么不让您的“ajax 操作”简单地填写所需的表单字段并提交表单呢?这样,您将获得与手动提交时完全相同的行为。

          【讨论】:

            【解决方案8】:

            这是另一种选择,尽管您需要进行一些跨浏览器测试:

            complete: function(request) {
                if(request.status == 200) {
                    var doc = document.open(request.getResponseHeader('Content-Type'));
                    doc.write(request.responseText);
                    doc.close();
                }
            }
            

            主要缺点:地址栏中的 URL 不变;可能会弄乱后退按钮/历史记录

            虽然我认为@crescentfresh 的想法是可行的方法

            【讨论】:

            • 好久没看到document.open()了。该代码将完全擦除当前页面,不是吗?有趣...
            • 是的,它只是将页面替换为 responseText。在 Firefox 和 Safari 上它也触发了 onload 事件,还没有在其他浏览器上尝试过。节省您对已有资源的另一个请求。
            猜你喜欢
            • 2013-07-26
            • 2017-02-18
            • 2012-06-30
            • 2020-02-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多