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

如何使用jQuery1.2.6管理Ajax调用的重定向指令?


当前回答

后端弹簧@ExceptionHandler。

400和业务相关异常的错误字符串(将在弹出窗口中显示)302和用于浏览器请求的应用程序异常的错误/登录页面的位置标头(由浏览器自动重定向)500/400和错误/登录页面的位置头,用于通过ajax回调重定向ajax请求

通过用户会话将异常详细信息传递到错误页

@Order(HIGHEST_PRECEDENCE)
public class ExceptionHandlerAdvise {

    private static Logger logger = LoggerFactory.getLogger(ExceptionHandlerAdvise.class);

    @Autowired
    private UserInfo userInfo;

    @ExceptionHandler(value = Exception.class)
    protected ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
        HttpHeaders headers = new HttpHeaders();
        if (isBusinessException(ex)) {
            logger.warn(getRequestURL(request), ex);
            return new ResponseEntity<>(getUserFriendlyErrorMessage(ex), headers, BAD_REQUEST);
        } else {
            logger.error(getRequestURL(request), ex);
            userInfo.setLastError(ex);
            headers.add("Location", "/euc-portal/fault");
            return new ResponseEntity<>(null, headers, isAjaxRequest(request) ? INTERNAL_SERVER_ERROR : FOUND);
        }
    }
}

private boolean isAjaxRequest(WebRequest request) {
    return request.getHeader("x-requested-with") != null;
}

private String getRequestURL(WebRequest request) {
    if (request instanceof ServletWebRequest) {
        HttpServletRequest servletRequest = ((ServletWebRequest) request).getRequest();
        StringBuilder uri = new StringBuilder(servletRequest.getRequestURI());
        if (servletRequest.getQueryString() != null) {
            uri.append("?");
            uri.append(servletRequest.getQueryString());
        }
        return uri.toString();
    }
    return request.getContextPath();
}

登录手柄接口

@Service
public class LoginHandlerInterceptor implements HandlerInterceptor {

    @Autowired
    private UserInfo userInfo;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (userInfo.getPrincipal() == null && !(request.getRequestURI().contains(LOGIN_URL) || request.getRequestURI().contains(FAULT_URL) || request.getRequestURI().startsWith("/app/css"))) {
            response.addHeader("Location", LOGIN_URL);
            response.setStatus(isAjaxRequest(request) ? BAD_REQUEST.value() : FOUND.value());
            return false;
        }
        return true;
    }
}

客户端代码

$.post('/app/request', params).done(function(response) {
    ...
}).fail(function(response) {
    if (response.getResponseHeader('Location')) {
        window.top.location.href = response.getResponseHeader('Location');
        return;
    }
    alert(response);
});

其他回答

我只想锁定整个页面的任何ajax请求@SuperG让我开始了。以下是我的结论:

// redirect ajax requests that are redirected, not found (404), or forbidden (403.)
$('body').bind('ajaxComplete', function(event,request,settings){
        switch(request.status) {
            case 301: case 404: case 403:                    
                window.location.replace("http://mysite.tld/login");
                break;
        }
});

我想特别检查某些http状态代码以作为我的决定的基础。然而,您可以绑定到ajaxError以获得除成功之外的任何结果(可能只有200?)

$('body').bind('ajaxError', function(event,request,settings){
    window.location.replace("http://mysite.tld/login");
}

大多数给定的解决方案都使用一种变通方法,使用额外的头或不合适的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);
    }
});

我通过以下方式解决了这个问题:

向响应中添加自定义标头:公共操作结果索引(){if(!HttpContext.User.Identity.IsAuthenticated){HttpContext.Response.AddHeader(“REQUIRES_AUTH”,“1”);}return View();}将JavaScript函数绑定到ajaxSuccess事件并检查标头是否存在:$(document).ajaxSuccess(函数(事件、请求、设置){if(request.getResponseHeader('REQUIRES_AUTH')==“1”){window.location='/';}});

我发现的另一个解决方案(如果要设置全局行为,尤其有用)是将$.ajaxsetup()方法与statusCode属性一起使用。正如其他人指出的,不要使用重定向状态码(3xx),而是使用4xx状态码并处理客户端重定向。

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

将400替换为要处理的状态代码。正如前面提到的401未经授权可能是个好主意。我使用400,因为它非常不具体,我可以在更具体的情况下使用401(比如错误的登录凭据)。因此,当会话超时并且您处理重定向客户端时,后端应该返回4xx错误代码,而不是直接重定向。即使使用backbone.js这样的框架,也非常适合我

我对标题解决方案没有任何成功——它们从未在我的ajaxSuccess/ajaxComplete方法中使用过。我在自定义响应中使用了Steg的答案,但我对JS端进行了一些修改。我设置了一个在每个函数中调用的方法,以便可以使用标准的$.get和$.post方法。

function handleAjaxResponse(data, callback) {
    //Try to convert and parse object
    try {
        if (jQuery.type(data) === "string") {
            data = jQuery.parseJSON(data);
        }
        if (data.error) {
            if (data.error == 'login') {
                window.location.reload();
                return;
            }
            else if (data.error.length > 0) {
                alert(data.error);
                return;
            }
        }
    }
    catch(ex) { }

    if (callback) {
        callback(data);
    }
}

使用中的示例。。。

function submitAjaxForm(form, url, action) {
    //Lock form
    form.find('.ajax-submit').hide();
    form.find('.loader').show();

    $.post(url, form.serialize(), function (d) {
        //Unlock form
        form.find('.ajax-submit').show();
        form.find('.loader').hide();

        handleAjaxResponse(d, function (data) {
            // ... more code for if auth passes ...
        });
    });
    return false;
}