我使用$.post()使用Ajax调用servlet,然后使用生成的HTML片段替换用户当前页面中的div元素。但是,如果会话超时,服务器将发送重定向指令,将用户发送到登录页面。在本例中,jQuery用登录页面的内容替换div元素,迫使用户的眼睛看到一个罕见的场景。
如何使用jQuery1.2.6管理Ajax调用的重定向指令?
我使用$.post()使用Ajax调用servlet,然后使用生成的HTML片段替换用户当前页面中的div元素。但是,如果会话超时,服务器将发送重定向指令,将用户发送到登录页面。在本例中,jQuery用登录页面的内容替换div元素,迫使用户的眼睛看到一个罕见的场景。
如何使用jQuery1.2.6管理Ajax调用的重定向指令?
当前回答
我用@John和@Arpad链接以及@RobWinch链接的答案得到了一份工作解决方案
我使用Spring Security 3.2.9和jQuery 1.10.2。
扩展Spring的类以仅从AJAX请求引起4XX响应:
public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public CustomLoginUrlAuthenticationEntryPoint(final String loginFormUrl) {
super(loginFormUrl);
}
// For AJAX requests for user that isn't logged in, need to return 403 status.
// For normal requests, Spring does a (302) redirect to login.jsp which the browser handles normally.
@Override
public void commence(final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException)
throws IOException, ServletException {
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
} else {
super.commence(request, response, authException);
}
}
}
applicationContext-security.xml
<security:http auto-config="false" use-expressions="true" entry-point-ref="customAuthEntryPoint" >
<security:form-login login-page='/login.jsp' default-target-url='/index.jsp'
authentication-failure-url="/login.jsp?error=true"
/>
<security:access-denied-handler error-page="/errorPage.jsp"/>
<security:logout logout-success-url="/login.jsp?logout" />
...
<bean id="customAuthEntryPoint" class="com.myapp.utils.CustomLoginUrlAuthenticationEntryPoint" scope="singleton">
<constructor-arg value="/login.jsp" />
</bean>
...
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
<property name="requestMatcher">
<bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
<constructor-arg>
<bean class="org.springframework.security.web.util.matcher.MediaTypeRequestMatcher">
<constructor-arg>
<bean class="org.springframework.web.accept.HeaderContentNegotiationStrategy"/>
</constructor-arg>
<constructor-arg value="#{T(org.springframework.http.MediaType).APPLICATION_JSON}"/>
<property name="useEquals" value="true"/>
</bean>
</constructor-arg>
</bean>
</property>
</bean>
在我的JSP中,添加一个全局AJAX错误处理程序,如下所示
$( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
if ( jqxhr.status === 403 ) {
window.location = "login.jsp";
} else {
if(thrownError != null) {
alert(thrownError);
} else {
alert("error");
}
}
});
此外,从JSP页面中的AJAX调用中删除现有的错误处理程序:
var str = $("#viewForm").serialize();
$.ajax({
url: "get_mongoDB_doc_versions.do",
type: "post",
data: str,
cache: false,
async: false,
dataType: "json",
success: function(data) { ... },
// error: function (jqXHR, textStatus, errorStr) {
// if(textStatus != null)
// alert(textStatus);
// else if(errorStr != null)
// alert(errorStr);
// else
// alert("error");
// }
});
我希望它能帮助其他人。
更新1我发现我需要将选项(始终使用默认target=“true”)添加到表单登录配置中。这是需要的,因为在AJAX请求被重定向到登录页面后(由于会话过期),Spring会记住之前的AJAX请求,并在登录后自动重定向到它。这将导致返回的JSON显示在浏览器页面上。当然,不是我想要的。
更新2不要总是使用默认target=“true”,而是使用@RobWinch示例来阻止来自requstCache的AJAX请求。这允许正常链接在登录后重定向到其原始目标,但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;
}
如果您使用的是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"));
}
}
来源:1, 2
在servlet中,您应该将response.setStatus(response.SC_MOVED_PERMANENTLY);要发送重定向所需的“301”xmlHttp状态。。。
在$.ajax函数中,不应使用.toString()函数。。。,只是
如果(xmlHttp.status==301){top.location.href='xxxx.jsp';}
问题是它不是很灵活,你无法决定要重定向到哪里。。
通过servlet重定向应该是最好的方法。但我仍然找不到正确的方法。
我这样解决了这个问题:
添加一个中间件来处理响应,如果是对ajax请求的重定向,则使用重定向url将响应更改为正常响应。
class AjaxRedirect(object):
def process_response(self, request, response):
if request.is_ajax():
if type(response) == HttpResponseRedirect:
r = HttpResponse(json.dumps({'redirect': response['Location']}))
return r
return response
然后在ajaxComplete中,如果响应包含重定向,则它必须是重定向,因此请更改浏览器的位置。
$('body').ajaxComplete(function (e, xhr, settings) {
if (xhr.status == 200) {
var redirect = null;
try {
redirect = $.parseJSON(xhr.responseText).redirect;
if (redirect) {
window.location.href = redirect.replace(/\?.*$/, "?next=" + window.location.pathname);
}
} catch (e) {
return;
}
}
}
我阅读了这个问题,并实现了所述的将响应HTTP状态代码设置为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”在服务器上构造为有两个成员:data.redirect和data.form。我发现这种方法要好得多。