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