题 如何在jQuery Ajax调用之后管理重定向请求


我在用着 $.post() 使用Ajax调用servlet,然后使用生成的HTML片段替换a div 用户当前页面中的元素。但是,如果会话超时,服务器会发送重定向指令以将用户发送到登录页面。在这种情况下,jQuery正在取代 div 元素与登录页面的内容,迫使用户的眼睛确实见证了罕见的场景。

如何使用jQuery 1.2.6从Ajax调用管理重定向指令?


1181
2017-10-13 21:29


起源


(不是这样的答案) - 我过去通过编辑jquery库并在每个XHR完成时添加对登录页面的检查来完成此操作。不是最好的解决方案,因为每次升级都必须这样做,但它确实解决了问题。 - Sugendran
查看相关问题: stackoverflow.com/questions/5941933/... - Nutel
该 HttpContext.Response.AddHeader 并检查ajaxsetup成功是要走的路 - Lijo
为什么服务器无法返回401?在这种情况下,您可以拥有全局$ .ajaxSetup并使用状态代码重定向页面。 - Vishal
这个链接 doanduyhai.wordpress.com/2012/04/21/... 给了我正确的解决方案 - pappu_kutty


答案:


我阅读了这个问题并实现了关于将响应状态代码设置为278的方法,以避免浏览器透明地处理重定向。虽然这有效,但我有点不满意,因为它有点像黑客。

经过更多的挖掘,我抛弃了这种方法并使用了 JSON。在这种情况下,对ajax请求的所有响应都具有状态代码200,并且响应主体包含在服务器上构造的JSON对象。然后,客户端上的javascript可以使用JSON对象来决定它需要做什么。

我遇到了类似的问题。我执行一个有两个可能响应的ajax请求:一个将浏览器重定向到一个新页面,另一个用新的替换当前页面上的现有HTML表单。执行此操作的jquery代码如下所示:

$.ajax({
    type: "POST",
    url: reqUrl,
    data: reqBody,
    dataType: "json",
    success: function(data, textStatus) {
        if (data.redirect) {
            // data.redirect contains the string URL to redirect to
            window.location.href = data.redirect;
        }
        else {
            // data.form contains the HTML for the replacement form
            $("#myform").replaceWith(data.form);
        }
    }
});

JSON对象“data”在服务器上构造为具有2个成员:data.redirect和data.form。我发现这种方法要好得多。


631
2017-10-07 22:54



正如解决方案中所述 stackoverflow.com/questions/503093/... 最好使用window.location.replace(data.redirect);比window.location.href = data.redirect; - Carles Barrobés
为什么在动作上使用HTTP代码不是更好的原因。例如,一个307代码是HTTP临时重定向? - Sergei Golos
@Sergei Golos的原因是,如果你进行HTTP重定向,重定向实际上永远不会到达ajax成功回调。浏览器处理重定向,提供包含重定向目标内容的200代码。 - Miguel Silva
仅当您对服务器有控制权时,此方法才有效 - h--n
如果它已经显示如何在服务器上完成此答案将更有帮助。 - Pedro Hoehl Carvalho


我解决了这个问题:

  1. 向响应添加自定义标头:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  2. 将JavaScript函数绑定到 ajaxSuccess 事件并检查标头是否存在:

    $(document).ajaxSuccess(function(event, request, settings) {
        if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
           window.location = '/';
        }
    });
    

213
2017-11-20 08:39



多么棒的解决方案。我喜欢一站式解决方案的想法。我需要检查403状态,但我可以在身体上使用ajaxSuccess绑定(这是我真正想要的。)谢谢。 - Bretticus
我刚刚做了这个,发现我需要ajaxComplete,我在使用$ .get()函数,并且200以外的任何状态都没有触发。事实上,我可能只是绑定到ajaxError。有关详细信息,请参阅下面的答案。 - Bretticus
在许多情况下都可以,但是如果您的框架处理授权会怎么样? - jwaliszko
我喜欢标题方法,但也想像@ mwoods79那样,重定向到哪里的知识不应该重复。我通过添加标题REDIRECT_LOCATION而不是布尔值来解决这个问题。 - rintcius
注意在重定向后的响应上设置标题。如本页其他答案中所述,重定向对于ajaxSucces处理程序可能是透明的。因此,我在登录页面的GET响应中包含了标题(最终只在我的场景中触发了ajaxSuccess)。 - sieppl


没有浏览器正确处理301和302响应。事实上,该标准甚至表示他们应该“透明地”处理它们,这对于Ajax Library供应商来说是一个非常令人头疼的问题。在 镭阿贾克斯 我们被迫使用HTTP响应状态代码278(只是一些“未使用的”成功代码)来处理来自服务器的透明重定向...

这真让我烦恼,如果有人在W3C有一些“拉”,我会很感激你可以让W3C 知道 我们真的需要自己处理301和302代码......! ;)


106
2018-01-27 18:10



我认为278应该成为官方HTTP规范的一部分。 - Chris Marisic
它是否已经透明地处理它们?如果资源已移动,则透明地处理它意味着在提供的URL上重复请求。这就是我期望使用XMLHttpRequest API。 - Philippe Rathé
@PhilippeRathé同意。透明处理正是我想要的。我不知道为什么它被认为是坏的。 - smwikipedia
@smwikipedia安排在主要部分重定向标记而不重定向页面。 - cmc


最终实现的解决方案是使用包装器来执行Ajax调用的回调函数,并在此包装器中检查返回的HTML块上是否存在特定元素。如果找到该元素,则包装器执行重定向。如果没有,则包装器将调用转发给实际的回调函数。

例如,我们的包装函数类似于:

function cbWrapper(data, funct){
    if($("#myForm", data).length > 0)
        top.location.href="login.htm";//redirection
    else
        funct(data);
}

然后,在进行Ajax调用时,我们使用了类似的东西:

$.post("myAjaxHandler", 
       {
        param1: foo,
        param2: bar
       },
       function(data){
           cbWrapper(data, myActualCB);
       }, 
       "html"
);

这对我们有用,因为所有Ajax调用总是在我们用来替换页面的DIV元素中返回HTML。此外,我们只需要重定向到登录页面。


85
2017-08-23 19:21



注意,这可以缩短为函数cbWrapper(funct){return function(data){if($(“#myForm”,data).size()> 0)top.location.href =“login”;其他功能(数据); }。然后在调用.post时只需要cbWrapper(myActualCB)。是的,评论中的代码很乱,但应该注意:) - Simen Echholt
尺寸已折旧,因此您可以使用.length代替尺寸 - sunil
我正在尝试这不适合我 - Koi To Hoga


我喜欢Timmerz的方法,略带柠檬味。如果你得到了回报 内容类型 的 text / html的 当你期待的时候 JSON,你最有可能被重定向。在我的情况下,我只是重新加载页面,它被重定向到登录页面。哦,并检查jqXHR状态是200,这看起来很愚蠢,因为你在错误函数中,对吧?否则,合法的错误情况将强制迭代重新加载(oops)

$.ajax(
   error:  function (jqXHR, timeout, message) {
    var contentType = jqXHR.getResponseHeader("Content-Type");
    if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
        // assume that our login has expired - reload our current page
        window.location.reload();
    }

});

57
2017-10-13 21:54



非常感谢Brian,你的答案对我的场景来说是最好的,尽管我想要更安全的检查,例如比较哪个url / page重定向而不是简单的“content-type”检查。我无法从jqXHR对象中找到重定向到哪个页面。 - Johnny
我检查状态401,然后重定向。像冠军一样工作。 - Eric


使用低级别 $.ajax() 呼叫:

$.ajax({
  url: "/yourservlet",
  data: { },
  complete: function(xmlHttp) {
    // xmlHttp is a XMLHttpRquest object
    alert(xmlHttp.status);
  }
});

试试这个重定向:

if (xmlHttp.code != 200) {
  top.location.href = '/some/other/page';
}

44
2018-05-23 10:02



我试图避免低级别的东西。无论如何,假设我使用了你描述的内容,当我检测到HTTP代码是3xx时,如何强制浏览器重定向?我的目标是重定向用户,而不仅仅是宣布他/她的会话已经过期。 - Elliot Vargas
顺便说一句,$ .ajax()不是非常非常低级。它只是jQuery方面的低级别,因为有$ .get,$ .post等比$ .ajax及其所有选项简单得多。 - Till
好家伙!对不起,我不得不“取消”你的答案,它仍然非常有帮助。事实上,重定向由XMLHttpRequest自动管理,因此我总是在重定向(叹息!)后获得200状态代码。我想我必须做一些讨厌的事情,比如解析HTML并找一个标记。 - Elliot Vargas
只是好奇,如果会话在服务器上结束,那是不是意味着服务器发送了不同的SESSIONID?难道我们不能发现那个吗? - Salamander2007
注意:这不适用于重定向。 ajax将转到新页面并返回其状态代码。


我只是想分享我的方法,因为这可能会对某人有所帮助:

我基本上包括一个JavaScript模块,它处理身份验证的东西,如显示用户名,以及处理 重定向到登录页面

我的场景:我们之间基本上有一个ISA服务器,它监听所有请求和 以302和位置标题响应 到我们的登录页面。

在我的JavaScript模块中我的 初步方法 有点像

$(document).ajaxComplete(function(e, xhr, settings){
    if(xhr.status === 302){
        //check for location header and redirect...
    }
});

问题(正如许多人已经提到的那样)是浏览器自己处理重定向 ajaxComplete 回调从来没有被召唤过,而是我得到了 已经重定向的登录页面的响应 这显然是一个 status 200。问题:如何检测成功的200响应是您的实际登录页面还是其他任意页面?

解决方案

由于我无法捕获302重定向响应,我添加了一个 LoginPage 我的登录页面上的标题,其中包含登录页面本身的URL。在模块中,我现在监听标题并进行重定向:

if(xhr.status === 200){
    var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
    if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
        window.location.replace(loginPageRedirectHeader);
    }
}

......这就像魅力:)你可能想知道为什么我把网址包括在内 LoginPage 标题...好吧基本上是因为我发现无法确定网址 GET 由自动位置重定向产生的 xhr 目的...


31
2018-05-06 23:55



+1 - 但自定义标题应该从一开始 X-,所以使用更好的标题 X-LoginPage: http://example.com/login。 - uınbɐɥs
@ShaquinTrifonoff不再了。我没有使用X-前缀,因为在 2011年6月 国际教育基金会的一份文件提出了他们的弃用,的确如此 2012年6月 自定义标题不应该以前缀为标题,这不是官方的 X-。 - Juri
我们还有一个ISA服务器,我遇到了同样的问题。我们不是在代码中解决它,而是使用了指令 kb2596444 配置ISA以停止重定向。 - scott stone


我知道这个话题很老,但我会给出另一种方法,我已经发现并且之前描述过 这里。基本上我正在使用带有WIF的ASP.MVC (但这对于本主题的上下文并不重要 - 无论使用哪个框架,答案都是充分的。线索保持不变 - 在执行ajax请求时处理与身份验证失败相关的问题)

下面显示的方法可以应用于开箱即用的所有ajax请求(如果他们没有明确重新定义发送事件之前)。

$.ajaxSetup({
    beforeSend: checkPulse,
    error: function (XMLHttpRequest, textStatus, errorThrown) {
        document.open();
        document.write(XMLHttpRequest.responseText);
        document.close();
    }
});

在执行任何ajax请求之前 CheckPulse 调用方法(控制器方法可以是最简单的):

[Authorize]
public virtual void CheckPulse() {}

如果用户未经过身份验证(令牌已过期),则无法访问此类方法(受保护 Authorize 属性)。因为框架处理身份验证,所以当令牌过期时,它会将http状态302置于响应中。如果您不希望浏览器透明地处理302响应,请在Global.asax中捕获它并更改响应状态 - 例如更改为200 OK。此外,添加标头,指示您以特殊方式处理此类响应(稍后在客户端):

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302
        && (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
    {                
        Context.Response.StatusCode = 200;
        Context.Response.AddHeader("REQUIRES_AUTH", "1");
    }
}

最后在客户端检查这样的自定义标头。如果存在 - 应该完成到登录页面的完全重定向(在我的情况下 window.location 被我的框架自动处理的请求中的url替换。

function checkPulse(XMLHttpRequest) {
    var location = window.location.href;
    $.ajax({
        url: "/Controller/CheckPulse",
        type: 'GET',
        async: false,
        beforeSend: null,
        success:
            function (result, textStatus, xhr) {
                if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
                    XMLHttpRequest.abort(); // terminate further ajax execution
                    window.location = location;
                }
            }
    });
}

26
2017-10-23 16:27



我通过使用PostAuthenticateRequest事件而不是EndRequest事件来解决问题。 - Frinavale
@JaroslawWaliszko我把错误的事件粘贴到我的上一个回复中!我的意思是PreSendRequestHeaders事件.....而不是PostAuthenticateRequest! >>腮红<<谢谢你指出我的错误。 - Frinavale
@JaroslawWaliszko使用WIF时,您也可以返回AJAX请求的401响应,并让您的javascript处理这些。此外,您假设所有302都需要在所有情况下都可能不正确的身份验证。如果有人有兴趣,我已经添加了答案。 - Rob


我认为更好的方法是利用现有的HTTP协议响应代码 401 Unauthorized

这是我解决它的方式:

  1. 服务器端:如果会话到期,请求是ajax。发送401响应代码头
  2. 客户端:绑定到ajax事件

    $('body').bind('ajaxSuccess',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    }).bind('ajaxError',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    });
    

IMO这是更通用的,你不是在写一些新的自定义规范/标题。您也不必修改任何现有的ajax调用。

编辑: Per @ Rob的评论如下,401(认证错误的HTTP状态代码)应该是指标。看到 403 Forbidden vs 401 Unauthorized HTTP响应 了解更多细节。有人说,一些Web框架使用403进行身份验证和授权错误 - 因此相应地进行调整。谢谢Rob。


21
2018-05-01 21:42



我使用相同的方法。 jQuery真的在403错误代码上调用ajaxSuccess吗?我认为实际上只需要ajaxError部分 - Marius Balčytis