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

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


当前回答

我对标题解决方案没有任何成功——它们从未在我的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;
}

其他回答

Try

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

把它放在登录页面上。如果它被加载到主页上的一个div中,它将重定向到登录页面。“#site”是位于除登录页面之外的所有页面上的div的id。

使用statusCode选项,如下所示,重定向通常是301、302状态代码。

$.ajax({
    type: <HTTP_METHOD>,
    url:  {server.url},
    data: {someData: true},
    statusCode: {
        301: function(responseObject, textStatus, errorThrown) {
            //yor code goes here
        },
        302: function(responseObject, textStatus, errorThrown) {
            //yor code goes here
        }           
    }
})
.done(function(data){
    alert(data);
})
.fail(function(jqXHR, textStatus){
    alert('Something went wrong: ' + textStatus);
})
.always(function(jqXHR, textStatus) {
   alert('Ajax request was finished')
});

后端弹簧@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);
});

我喜欢蒂默兹的方法,稍微加一点柠檬。如果在期望JSON时返回text/html的contentType,则很可能会被重定向。在我的例子中,我只需重新加载页面,它就会被重定向到登录页面。哦,检查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();
    }

});

有些人可能会发现以下内容很有用:

我希望客户机被重定向到登录页面,以执行在没有授权令牌的情况下发送的任何rest操作。由于我的所有其余操作都是基于Ajax的,所以我需要一种很好的通用方法来重定向到登录页面,而不是处理Ajax成功函数。

这是我所做的:

在任何Ajax请求中,我的服务器都会返回Json 200响应“需要认证”(如果客户端需要认证)。

Java(服务器端)中的简单示例:

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private final Logger m_logger = LoggerFactory.getLogger(AuthenticationFilter.class);

    public static final String COOKIE_NAME = "token_cookie"; 

    @Override
    public void filter(ContainerRequestContext context) throws IOException {        
        // Check if it has a cookie.
        try {
            Map<String, Cookie> cookies = context.getCookies();

            if (!cookies.containsKey(COOKIE_NAME)) {
                m_logger.debug("No cookie set - redirect to login page");
                throw new AuthenticationException();
            }
        }
        catch (AuthenticationException e) {
            context.abortWith(Response.ok("\"NEED TO AUTHENTICATE\"").type("json/application").build());
        }
    }
}

在我的Javascript中,我添加了以下代码:

$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    var originalSuccess = options.success;

    options.success = function(data) {
        if (data == "NEED TO AUTHENTICATE") {
            window.location.replace("/login.html");
        }
        else {
            originalSuccess(data);
        }
    };      
});

就这样。