如何使用jQuery防止Ajax请求跟随重定向

116

我使用jQuery ajax函数来访问web服务,但服务器不返回一个状态代码描述问题的响应,而是将请求重定向到一个带有200头的页面,描述了问题。我不能做任何更改,所以我需要在客户端上解决它。

例如: 发送到某个URL的请求未找到,因此我收到302重定向到另一个位置。发送新请求后,我收到200 OK,从而防止错误回调触发。

是否有办法防止ajax请求跟随重定向,而是调用回调,最好是error方法。 或者,是否可以检测客户端是否发生了重定向?


这可能会有所帮助:https://dev59.com/IkrSa4cB1Zd3GeqPXIcs - PiTheNumber
9
你的后端报告了302状态码,而不是404状态码,这很奇怪,因为它表示资源未找到。我会帮助你将其翻译成中文并保留原意,但不会添加解释或其他内容。 - Jake Feasel
1
可能是 Prevent redirection of Xmlhttprequest 的重复问题。 - user
9个回答

105

我认为你的问题很有趣,但整个问题似乎更多是一个误解。至少我会尝试解释我的理解。

静默(透明)重定向是XMLHttpRequest规范的一部分(请参见这里,特别是“…透明地跟随重定向…”词语)。标准只提到了用户代理(Web浏览器)可能防止或通知某些自动重定向的种类,但这不是XMLHttpRequest的一部分。这是HTTP客户端配置(操作系统配置)或Web浏览器配置的一部分。因此,jQuery.ajax没有任何选项可以阻止重定向。

你可以看到HTTP重定向是HTTP协议的一部分,而不是XMLHttpRequest的一部分。因此它在另一个抽象级别或网络堆栈上。例如,可以从HTTP代理或本地浏览器缓存中检索XMLHttpRequest的数据,并且这是HTTP协议的一部分。大多数情况下,提供数据的是服务端而不是客户端,可以影响缓存。

你可以将你的要求与防止Web服务器的IP地址更改或在通信期间更改IP路由的要求进行比较。所有这些都可以在某些情况下很有趣,但它们是另一个通信堆栈级别的部分,无法由jQuery.ajaxXMLHttpRequest管理。

XMLHttpRequest标准规定客户端配置可以有选项来防止重定向。在“微软世界”中,我更熟悉的是可以查看WinHttpSetOption函数,该函数可用于设置WINHTTP_DISABLE_REDIRECTS值的WINHTTP_OPTION_DISABLE_FEATURE选项。另一种方法是使用WINHTTP_OPTION_REDIRECT_POLICY选项和WINHTTP_OPTION_REDIRECT_POLICY_NEVER值。还有一个在Windows中可以使用的功能是WinHttpSetStatusCallback函数,它可以设置回调函数接收一些通知,例如WINHTTP_CALLBACK_FLAG_REDIRECT
因此,一般情况下可以实现您的要求,但解决方案可能与操作系统或Web浏览器不独立,并且不在jQuery.ajaxXMLHttpRequest级别上。

3
非常好的答案,见解深刻!我有使用 curl 的经验,你提到的设置类似的标志。我希望这样的指令也可以传递给浏览器,但从你的回答中,我明白了期望的行为应该是跟随重定向,就像最初的请求被发送到服务器分配的位置一样。 - Jørgen
@Jørgen:为什么重定向在你的情况下是个问题?如果服务器将某些页面暂时或永久地移动到另一个位置,它可以将您的原始请求重定向到新位置。这完全没问题。管理员可以配置Web服务器,在服务器上进行还原操作或任何其他支持工作期间执行重定向。如果您通过DNS URL询问服务器,则管理员可以更改IP映射到另一台服务器。他可以通过HTTP重定向以与DNS重新配置相同的方式来处理。你的问题是什么? - Oleg
然而,某些浏览器实现(如Safari)已知会修改或添加重定向请求的标头,如果这些添加的标头与服务器策略不兼容,则可能会导致错误。 - Tyler Biscoe
我觉得这篇文章有误导性,它试图表达的是服务器在响应原始请求时自动“生成”第二个请求,并将第二个自动请求的响应附加到重定向响应中。就像服务器同时返回两个响应,但HTTP 1.1标准明确规定客户端自己发起第二个请求,并且在某些情况下可以选择不发起请求。这与服务器IP更改非常不同,仅仅是XMLHttpRequest标准的限制。 - snovity
@snovity:抱歉,但问题是如何通过使用jQuery ajax的某些选项来防止重定向。答案是:这是不可能的,甚至在底层XMLHttpRequest的级别上也是不可能的。我知道你不喜欢它。我也不喜欢。但是,使用WinHttpSetOption API(无法在JavaScript中使用,仅存在于Windows上)是我知道防止重定向的唯一方法。此外,我认为您误解了一件事情:重定向使客户端而不是服务器:服务器只返回指令以进行重定向 - Oleg
显示剩余6条评论

27

我不认为这是可能的。底层库(XHR)会自动透明地进行新请求。话虽如此,在这种情况下我已经做过的事情(通常是会话超时类型的问题,会将我带到一个登录页面),是发送自定义响应头。我还设置了一个全局ajax处理程序来检查该标头的存在,并在出现时作出适当的响应(例如,将整个页面重定向到登录屏幕)。

如果你有兴趣,这里是我用于监视该自定义标头的jQuery代码:

/* redirects main window when AJAX request indicates that the session has expired on the backend. */
function checkSession(event, xhr, ajaxOptions)
{
    if (xhr.readyState == 4)
    {
        if(xhr.getResponseHeader("Login-Screen") != null && xhr.getResponseHeader("Login-Screen").length)
        {
            window.location.href='sessionExpired.html'; //whatever
        }
    }
}

$(document).ajaxComplete(checkSession)

1
谢谢,我想我只能采用类似的方法,分析响应。 - Jørgen

12
我找到了一个功能,可以检查你的电话是否已被重定向。它是xhr.state():如果它是“rejected”,那么就发生了重定向。
成功回调的示例:

我发现一个检查电话是否已经被重定向的特性。 它是xhr.state():如果它是“rejected”,则表示已经被重定向。

成功回调的示例:

request.success(function(data, textStatus, xhr)
{
    if(xhr.state() == "resolved")
    {
        //no redirection
    }
    if(xhr.state() == "rejected")
    {
        //redirection
    }
});

带有错误回调的示例:

request.error(function(xhr, textStatus)
{
    if (xhr.state() == "rejected")
    {
        //redirection
        location.href = "loginpage";
    } else
    {
        //some other error happened
        alert("error");
    }
});

1
你的回答中的提示真的帮助了我们解决问题。为了记录可以帮助其他人的笔记,我们有一个将其安全性与SiteMinder集成的WebSphere Portal。我们有一个需要通过Ajax调用资源URL的Portlet,当它超时时,会发生透明重定向,但我们无法掌握如何重定向到登录页面。检查jqXHR.state()是否为“rejected”确实有所帮助。再次感谢。 - Uresh K
很好的发现,但是可能会有误报,因为“rejected”状态可能会在承诺被拒绝时触发,而不仅仅是重定向。jQuery解释了何时发送“rejected”状态https://api.jquery.com/deferred.state/ - Nitin

2

我无法像之前回答的有见地的程序员那样增加内容,但我会补充一个特定的案例,其他人可能会发现这很有用。

我在SharePoint上遇到了这个302静默重定向。我有一些简单的Javascript客户端代码,可以ping SharePoint子站点,如果收到200 HTTP响应,则通过window.location将其重定向到该站点。如果收到其他任何响应,则向用户发出通知,说明该站点不存在。

然而,在站点存在但用户没有权限的情况下,SharePoint会静默重定向到AccessDenied.aspx页面。SharePoint已经在服务器/农场级别完成了HTTP 401身份验证握手 - 用户可以访问SharePoint。但是,对子站点的访问使用某种类型的数据库标志进行处理。静默重定向绕过了我的“else”子句,因此我无法显示自己的错误。在我的情况下,这不是一个停机点 - 这是一致可预测的行为。但这有点令人惊讶,并且我在这个过程中学到了关于HTTP请求的一些东西!


2

虽然无法禁用 XmlHttpRequests 中的位置重定向跟随,但在使用 fetch() 时可以实现:

fetch('url', {redirect: manual});

Fetch不会告诉你被重定向的URL是什么。你可以禁用这种行为,但“redirect: manual”并不意味着让用户自己处理重定向,因为它的名称表明了意图。 - lcjury

1

我对同样的事情感兴趣,但找不到被 Takman 提到的 state() 方法(链接),因此进行了一些调查。为了让搜索答案的人能够在这里找到,以下是我的发现:

正如多次声明的那样,您无法阻止重定向,但可以检测它们。根据 MDN 的说法,您可以使用 XMLHttpRequestObjectresponseURL,它将包含响应来自的最终 URL,在所有重定向之后。唯一的限制是它不受 Internet Explorer 支持(Edge 有)。由于传递到 jquery 的 success/done 函数的 xhr/jqXHR 是实际 XMLHttpRequest 的扩展,因此也应该在那里可用。


0
我猜你之所以收到200响应,是因为第二次没有重定向,因为404页面没有过期,它保存在缓存中。也就是说,第二次浏览器会直接从缓存中返回给你页面。 在ajax jquery中有一个名为“cache”的属性。 http://api.jquery.com/jQuery.ajax/ 你应该将它设为“false”。

-2

我不确定这是否适用于你的情况,但是你可以编写代码来响应AJAX函数中的特定状态码 -

$.ajax({
    url: '/admin/secret/data',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    statusCode: {
        200: function (data) {
            alert('302: Occurred');
            // Bind the JSON data to the UI
        },
        401: function (data) {
            alert('401: Occurred');
            // Handle the 401 error here.
        }
    }
});

9
我认为这并不适用,因为 OP 说由于后台重定向,他总是收到 200 状态代码。 - Yes Barry

-4
在 AJAX 请求的请求头中,你将会有以下内容:
X-Requested-With    XMLHttpRequest

通过这个标准,你可以在服务器端过滤请求。


他想检查响应而不是请求(对于请求,他已经有了控制)。 - Yes Barry
也许Gfox建议对于返回3xx的ajax请求是毫无意义的,你可以过滤这种请求并返回403。当你有一个网站提供需要表单认证的页面,并且这些页面还进行ajax调用,而你想把授权逻辑放在一个地方时,对我来说这是一个有效的答案。 - maciejW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接