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

JavaScript

Pro小胖

2020-03-09

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

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

第274篇《jQuery Ajax调用后如何管理重定向请求》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

12个回答
GO凯 2020.03.09

You can also hook XMLHttpRequest send prototype. This will work for all sends (jQuery/dojo/etc) with one handler.

I wrote this code to handle a 500 page expired error, but it should work just as well to trap a 200 redirect. Ready the wikipedia entry on XMLHttpRequest onreadystatechange about the meaning of readyState.

// Hook XMLHttpRequest
var oldXMLHttpRequestSend = XMLHttpRequest.prototype.send;

XMLHttpRequest.prototype.send = function() {
  //console.dir( this );

  this.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 500 && this.responseText.indexOf("Expired") != -1) {
      try {
        document.documentElement.innerHTML = this.responseText;
      } catch(error) {
        // IE makes document.documentElement read only
        document.body.innerHTML = this.responseText;
      }
    }
  };

  oldXMLHttpRequestSend.apply(this, arguments);
}
L泡芙Jim 2020.03.09

If you also want to pass the values then you can also set the session variables and access Eg: In your jsp you can write

<% HttpSession ses = request.getSession(true);
   String temp=request.getAttribute("what_you_defined"); %>

And then you can store this temp value in your javascript variable and play around

米亚凯 2020.03.09

如果您使用的是Spring Security,答案似乎对某些人有用,但我发现可以扩展LoginUrlAuthenticationEntryPoint并添加特定的代码来更健壮地处理AJAX。大多数示例拦截所有重定向,而不仅仅是身份验证失败。对于我从事的项目来说,这是不可取的。如果您不希望缓存失败的AJAX请求,则可能还需要扩展ExceptionTranslationFilter并重写“ sendStartAuthentication”方法以删除缓存步骤。

示例AjaxAwareAuthenticationEntryPoint:

public class AjaxAwareAuthenticationEntryPoint extends
    LoginUrlAuthenticationEntryPoint {

    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
        super(loginUrl);
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        if (isAjax(request)) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), "Please re-authenticate yourself");
        } else {
        super.commence(request, response, authException);
        }
    }

    public static boolean isAjax(HttpServletRequest request) {
        return request != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    }
}

来源: 12

null 2020.03.09

尝试

    $(document).ready(function () {
        if ($("#site").length > 0) {
            window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
        }
    });

将其放在登录页面上。如果已将其加载到主页的div中,它将重定向到登录页面。“ #site”是位于除登录页面以外的所有页面上的div的ID。

GilGreen 2020.03.09

我有一个适用于我的简单解决方案,无需更改服务器代码...只需添加一小撮肉豆蔻...

$(document).ready(function ()
{
    $(document).ajaxSend(
    function(event,request,settings)
    {
        var intercepted_success = settings.success;
        settings.success = function( a, b, c ) 
        {  
            if( request.responseText.indexOf( "<html>" ) > -1 )
                window.location = window.location;
            else
                intercepted_success( a, b, c );
        };
    });
});

我检查html标签的存在,但是您可以更改indexOf来搜索登录页面中存在的唯一字符串...

米亚小小神乐 2020.03.09

给定的大多数解决方案都使用一种变通方法,即使用额外的标头或不正确的HTTP代码。这些解决方案很可能会起作用,但会有点“棘手”。我想出了另一个解决方案。

我们使用的WIF已配置为在401响应上重定向(passiveRedirectEnabled =“ true”)。重定向在处理普通请求时很有用,但不适用于AJAX请求(因为浏览器不会执行302 /重定向)。

在global.asax中使用以下代码,可以禁用AJAX请求的重定向:

    void WSFederationAuthenticationModule_AuthorizationFailed(object sender, AuthorizationFailedEventArgs e)
    {
        string requestedWithHeader = HttpContext.Current.Request.Headers["X-Requested-With"];

        if (!string.IsNullOrEmpty(requestedWithHeader) && requestedWithHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
        {
            e.RedirectToIdentityProvider = false;
        }
    }

这样,您就可以为AJAX请求返回401响应,然后您的JavaScript可以通过重新加载页面来处理该响应。重新加载页面将抛出401,将由WIF处理(WIF会将用户重定向到登录页面)。

处理401错误的示例JavaScript:

$(document).ajaxError(function (event, jqxhr, settings, exception) {

    if (jqxhr.status == 401) { //Forbidden, go to login
        //Use a reload, WIF will redirect to Login
        location.reload(true);
    }
});
A小胖 2020.03.09

我发现的另一种解决方案(如果要设置全局行为特别有用)是将该$.ajaxsetup()方法statusCodeproperty一起使用就像其他人指出的那样,不要使用重定向状态码(3xx),而应使用状态码来4xx处理客户端重定向。

$.ajaxSetup({ 
  statusCode : {
    400 : function () {
      window.location = "/";
    }
  }
});

替换400为您要处理的状态码。像已经提到的那样,401 Unauthorized可能是一个好主意。我使用,400因为它是非常不确定的,我可以将其401用于更具体的情况(例如错误的登录凭据)。因此,4xx当会话超时并且您要处理客户端重定向时,后端应该返回错误代码,而不是直接重定向。即使使用骨干.js之类的框架,对我来说也很完美

老丝Tom 2020.03.09

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

我基本上包括一个JavaScript模块,该模块处理身份验证之类的内容,例如显示用户名,在这种情况下,还处理重定向到登录页面的情况

我的情况是:我们基本上有一个ISA服务器,其间侦听所有请求并以302和登录页面的位置标头作为响应

在我的JavaScript模块中,我最初的方法

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

问题(这里已经提到了很多问题)是浏览器自己处理重定向,因此ajaxComplete从未调用过我的回调,而是获得了已经重定向的Login页面响应,显然是a status 200问题:您如何检测成功的200响应是您的实际登录页面还是其他任意页面?

解决方案

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

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

...就像魅​​力:)。您可能想知道为什么我在LoginPage标头中包含url ...基本上是因为我找不到确定GETxhr对象的自动位置重定向得到的url的方法...

达蒙小胖 2020.03.09

我喜欢蒂默兹的方法,加上一点柠檬味。如果你有机会返回的contentType的text / html,当你期待JSON,你是最有可能被重定向。就我而言,我只是简单地重新加载页面,然后将其重定向到登录页面。哦,检查一下jqXHR的状态是否为200,这似乎很愚蠢,因为您使用的是错误函数,对吗?否则,合法的错误情况将迫使迭代重载(哎呀)

$.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();
    }

});
神无老丝Davaid 2020.03.09

没有浏览器正确处理301和302响应。实际上,该标准甚至说他们应该“透明地”处理它们,这对于Ajax Library供应商来说是一个巨大的麻烦。Ra-Ajax中,我们被迫使用HTTP响应状态代码278(只是一些“未使用”的成功代码)来透明地处理来自服务器的重定向。

这真的让我很烦,如果这里有人在W3C中有一些“拉”,我将不胜感激,您可以让W3C 知道我们确实需要自己处理301和302代码...!;)

蛋蛋猿 2020.03.09

最终实现的解决方案是对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,该DIV元素用于替换页面的一部分。另外,我们只需要重定向到登录页面。

猪猪路易 2020.03.09

我阅读了此问题,并实现了关于将响应HTTP状态代码设置为278的方法,以避免浏览器透明地处理重定向。即使这样做有效,我还是有点不满意,因为它有点破烂。

在深入研究之后,我放弃了这种方法并使用了JSON在这种情况下,所有对AJAX请求的响应都具有状态码 200,并且响应的主体包含在服务器上构造的JSON对象。然后,客户端上的JavaScript可以使用JSON对象来决定它需要做什么。

我有一个与您类似的问题。我执行的AJAX请求有2种可能的响应:一种浏览器重定向到新页面,另一种新页面替换当前页面上的现有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对象“数据”在服务器上构造为具有2个成员:data.redirectdata.form我发现这种方法要好得多。

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android