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

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

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

1092857 次浏览

使用低级$.ajax()调用:

$.ajax({url: "/yourservlet",data: { },complete: function(xmlHttp) {// xmlHttp is a XMLHttpRquest objectalert(xmlHttp.status);}});

试试这个重定向:

if (xmlHttp.code != 200) {top.location.href = '/some/other/page';}

此外,您可能希望将用户重定向到给定的标头URL。所以最后它看起来像这样:

$.ajax({//.... other definitioncomplete:function(xmlHttp){if(xmlHttp.status.toString()[0]=='3'){top.location.href = xmlHttp.getResponseHeader('Location');}});

UPD:是的。有同样的任务,但它不起作用。做这些东西。当我找到它时,我会向你展示解决方案。

在servlet中,您应该放置response.setStatus(response.SC_MOVED_PERMANENTLY);发送重定向所需的“301”xmlHttp状态…

在$. ajax函数中,您不应该使用.toString()函数…,只需

if(xmlHttp.status==301){top.location.href='xxxx.jsp';

问题是它不是很灵活,你不能决定你想重定向到哪里。

通过servlet重定向应该是最好的方法。但我仍然找不到正确的方法来做到这一点。

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

这真的让我很恼火,如果这里有人在W3C中有一些“拉动”,我希望你能让W3C知道知道我们真的需要自己处理301和302代码…!;)

最终实现的解决方案是为Ajax调用的回调函数使用包装器,并在此包装器中检查返回的超文本标记语言块上是否存在特定元素。如果找到该元素,则包装器执行重定向。如果没有,包装器将调用转发到实际的回调函数。

例如,我们的包装函数类似于:

function cbWrapper(data, funct){if($("#myForm", data).length > 0)top.location.href="login.htm";//redirectionelsefunct(data);}

然后,在进行Ajax调用时,我们使用了以下内容:

$.post("myAjaxHandler",{param1: foo,param2: bar},function(data){cbWrapper(data, myActualCB);},"html");

这对我们很有效,因为所有Ajax调用总是在我们用来替换页面的DIV元素中返回超文本标记语言。此外,我们只需要重定向到登录页面。

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

  1. 在响应中添加自定义标头:

    public ActionResult Index(){if (!HttpContext.User.Identity.IsAuthenticated){HttpContext.Response.AddHeader("REQUIRES_AUTH","1");}return View();}
  2. Binding a JavaScript function to the ajaxSuccess event and checking to see if the header exists:

    $(document).ajaxSuccess(function(event, request, settings) {if (request.getResponseHeader('REQUIRES_AUTH') === '1') {window.location = '/';}});

把弗拉基米尔·普鲁德尼科夫和托马斯·汉森所说的话放在一起:

  • 更改您的服务器端代码以检测它是否是XHR。如果是,请将重定向的响应代码设置为278。在django中:
   if request.is_ajax():response.status_code = 278

这使得浏览器将响应视为成功,并将其交给您的Javascript。

  • 在您的JS中,确保表单提交是通过Ajax提交的,检查响应代码并在需要时重定向:
$('#my-form').submit(function(event){
event.preventDefault();var options = {url: $(this).attr('action'),type: 'POST',complete: function(response, textStatus) {if (response.status == 278) {window.location = response.getResponseHeader('Location')}else { ... your code here ... }},data: $(this).serialize(),};$.ajax(options);});

我阅读了这个问题,并实施了关于将响应HTTP状态码设置为278的方法,以避免浏览器透明地处理重定向。尽管这有效,我有点不满意,因为它有点黑客。

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

我遇到了和你类似的问题。我执行了一个AJAX请求,它有两种可能的响应:一种是重定向浏览器指向新页面,另一种是取代当前页面上现有的超文本标记语言表单具有新表单。执行此操作的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 towindow.location.href = data.redirect;} else {// data.form contains the HTML for the replacement form$("#myform").replaceWith(data.form);}}});

JSON对象“data”在服务器上构造为具有2个成员:data.redirectdata.form。我发现这种方法要好得多。

我只是想锁定整个页面的任何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;}});

我想专门检查某些超文本传输协议状态代码来做出决定。然而,你可以绑定到ajaxError来获得除成功之外的任何东西(也许只有200?)我可以写:

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

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

$(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;elseintercepted_success( a, b, c );};});});

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

试试看

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

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

我喜欢Timmerz稍微扭曲的方法。如果你在期待JSON的时候得到属性类型text/html返回,你很可能被重定向了。在我的情况下,我只是简单地重新加载页面,它被重定向到登录页面。哦,检查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 pagewindow.location.reload();}
});

我在一个我正在修补的django应用程序上遇到这个问题(免责声明:我正在修补学习,我不是专家)。我想做的是使用jQuery ajax向资源发送DELETE请求,在服务器端删除它,然后将重定向发送回(基本上)主页。当我从python脚本发送HttpResponseRedirect('/the-redirect/')时,jQuery的ajax方法接收的是200而不是302。所以,我所做的是发送300的响应:

response = HttpResponse(status='300')response['Location'] = '/the-redirect/'return  response

然后我在客户端上发送/处理请求,jQuery.ajax如下:

<button onclick="*the-jquery*">Delete</button>
where *the-jquery* =$.ajax({type: 'DELETE',url: '/resource-url/',complete: function(jqxhr){window.location = jqxhr.getResponseHeader('Location');}});

也许使用300不是“正确的”,但至少它像我想要的那样工作。

PS:在SO的移动版本上编辑是一个巨大的痛苦。愚蠢的ISP在我回答完后就把我的服务取消请求通过了!

    <script>function showValues() {var str = $("form").serialize();$.post('loginUser.html',str,function(responseText, responseStatus, responseXML){if(responseStatus=="success"){window.location= "adminIndex.html";}});}</script>

我解决了这个问题,把下面的在我的login.php页面。

<script type="text/javascript">if (top.location.href.indexOf('login.php') == -1) {top.location.href = '/login.php';}</script>

我这样解决了这个问题:

添加中间件来处理响应,如果它是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 rreturn response

然后在ajax完整中,如果响应包含重定向,则它必须是重定向,因此更改浏览器的位置。

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

您还可以挂钩XMLHttpRequest发送原型。这将适用于所有发送(jQuery/dojo/etc)和一个处理程序。

我编写此代码是为了处理500页过期错误,但它应该也可以捕获200重定向。准备好维基百科条目XMLHttpRequest相关文档 onreadystatange关于readyState的含义。

// Hook XMLHttpRequestvar 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 onlydocument.body.innerHTML = this.responseText;}}};
oldXMLHttpRequestSend.apply(this, arguments);}

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

我基本上包含了一个JavaScript模块,它处理身份验证的东西,比如显示用户名,还有这个处理重定向到登录页面的案例。

我的场景:我们基本上有一个ISA服务器,它监听所有请求,以302和位置头响应监听我们的登录页面。

在我的JavaScript模块中,我的初步方法类似于

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

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

的解决方案

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

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

…这就像魅力:)。您可能想知道为什么我在LoginPage标头中包含url…基本上是因为我找不到确定GET的url的方法,这是由xhr对象的自动位置重定向产生的…

我认为处理这个问题的更好方法是利用现有的HTTP协议响应代码,特别是401 Unauthorized

我是这样解决的:

  1. 服务器端:如果会话过期,并且请求是ajax。发送401响应代码头
  2. 客户端:绑定到ajax事件

    $('body').bind('ajaxSuccess',function(event,request,settings){if (401 == request.status){window.location = '/users/login';}}).bind('ajaxError',function(event,request,settings){if (401 == request.status){window.location = '/users/login';}});

IMO this is more generic and you are not writing some new custom spec/header. You also should not have to modify any of your existing ajax calls.

Edit: Per @Rob's comment below, 401 (the HTTP status code for authentication errors) should be the indicator. See 403 Forbidden vs 401 Unauthorized HTTP responses for more detail. With that being said some web frameworks use 403 for both authentication AND authorization errors - so adapt accordingly. Thanks Rob.

如果您还想传递值,那么您还可以设置会话变量并访问例如:在您的jsp中,您可以编写

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

然后你可以将这个临时值存储在你的JavaScript变量中

我在标头解决方案上没有任何成功——它们从未在我的ajax成功/ajax完整方法中被选中。我在自定义响应中使用了Steg的答案,但我对JS端进行了一些修改。我设置了一个在每个函数中调用的方法,这样我就可以使用标准的$.get$.post方法。

function handleAjaxResponse(data, callback) {//Try to convert and parse objecttry {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 formform.find('.ajax-submit').hide();form.find('.loader').show();
$.post(url, form.serialize(), function (d) {//Unlock formform.find('.ajax-submit').show();form.find('.loader').hide();
handleAjaxResponse(d, function (data) {// ... more code for if auth passes ...});});return false;}

我知道这个主题很老,但我将给出另一种方法,我已经找到并在前面描述了这里

下面所示的方法可以应用于开箱即用的所有ajax请求(如果它们没有明显地重新定义beFore发送事件)。

$.ajaxSetup({beforeSend: checkPulse,error: function (XMLHttpRequest, textStatus, errorThrown) {document.open();document.write(XMLHttpRequest.responseText);document.close();}});

在执行任何ajax请求之前,调用CheckPulse方法(控制器方法可以是任何最简单的方法):

[Authorize]public virtual void CheckPulse() {}

如果user未通过身份验证(令牌已过期),则无法访问此类方法(受Authorize属性保护)。因为框架处理身份验证,当令牌过期时,它会将超文本传输协议状态302置于响应中。如果您不希望浏览器透明地处理302响应,请在Global.asax捕获它并更改响应状态-例如更改为200 OK。此外,添加标头,指示您以特殊方式处理此类响应(稍后在客户端):

protected void Application_EndRequest(){if (Context.Response.StatusCode == 302&& (new HttpContextWrapper(Context)).Request.IsAjaxRequest()){Context.Response.StatusCode = 200;Context.Response.AddHeader("REQUIRES_AUTH", "1");}}

最后在客户端检查此类自定义标头。如果存在-应该完成对登录页面的完全重定向(在我的情况下,window.location被来自请求的url替换,该请求由我的框架自动处理)。

function checkPulse(XMLHttpRequest) {var location = window.location.href;$.ajax({url: "/Controller/CheckPulse",type: 'GET',async: false,beforeSend: null,success:function (result, textStatus, xhr) {if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {XMLHttpRequest.abort(); // terminate further ajax executionwindow.location = location;}}});}

虽然答案似乎适用于使用Spring Security的人,但我发现扩展LoginUrlAuthentiationEntryPoint并添加特定代码来处理AJAX更健壮。大多数示例拦截所有重定向不仅仅是身份验证失败。这对于我工作的项目来说是不可取的。如果您不希望缓存失败的AJAX请求,您可能会发现还需要扩展ExceptionTranslationFilter并覆盖“sendStartAuthentiation ation”方法来删除缓存步骤。

示例AjaxAware身份验证EntryPoint:

public class AjaxAwareAuthenticationEntryPoint extendsLoginUrlAuthenticationEntryPoint {
public AjaxAwareAuthenticationEntryPoint(String loginUrl) {super(loginUrl);}
@Overridepublic 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

大多数给定的解决方案都使用变通方法,使用额外的标头或不合适的HTTP代码。这些解决方案很可能有效,但感觉有点“hacky”。我想出了另一个解决方案。

我们使用的WIF配置为在401响应上重定向(passiveReDirectEn的="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,该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 Loginlocation.reload(true);}});

这对我有用:

success: function(data, textStatus, xhr) {
console.log(xhr.status);}

成功后,ajax将获得浏览器从服务器获得的相同状态代码并执行它。

我发现的另一个解决方案(如果你想设置全局行为,特别有用)是将#0方法#1属性一起使用。就像其他人指出的那样,不要使用重定向状态代码(3xx),而是使用4xx状态代码并处理重定向客户端。

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

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

最后,我通过添加自定义HTTP Header来解决问题。就在服务器端每个请求的响应之前,我将当前请求的url添加到响应的标头中。

我在服务器上的应用程序类型是Asp.Net MVC,它有一个很好的地方可以做到这一点。在Global.asax中,我实现了Application_EndRequest事件,因此:

    public class MvcApplication : System.Web.HttpApplication{
//  ...//  ...
protected void Application_EndRequest(object sender, EventArgs e){var app = (HttpApplication)sender;app.Context.Response.Headers.Add("CurrentUrl",app.Context. Request.CurrentExecutionFilePath);}
}

它非常适合我!现在在JQuery$.post的每个响应中,我都有请求的url和其他响应标头,这些标头是POST方法通过状态302303、…产生的。

其他重要的事情是不需要修改服务器端和客户端的代码。

而下一个是能够获得访问其他信息后的行动,如错误,消息,和…,以这种方式。

我发布了这个,也许可以帮助别人:)

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

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

这就是我所做的:

在任何Ajax请求上,我的服务器都会返回一个Json 200响应“NEED TO AUTHENTICATE”(如果客户端需要进行身份验证)。

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";
@Overridepublic 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);}};});

这就是它。

使用ASP.NETMVC重定向方法时可能会出现此问题。为了防止在div中显示响应的形式,您可以简单地执行某种ajax响应过滤器以输入$. ajaxSetup配置文件的响应。如果响应包含MVC重定向,您可以在JS侧评估此表达式。下面是JS的示例代码:

$.ajaxSetup({dataFilter: function (data, type) {if (data && typeof data == "string") {if (data.indexOf('window.location') > -1) {eval(data);}}return data;}});

如果data是:"window.location='/Acount/Login'"以上过滤器将捕获并评估重定向,而不是让数据显示。

我用@John和@Arpad链接和@RobWink链接的答案得到了一个有效的解决方案

我使用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.@Overridepublic 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");//            }});

我希望它能帮助其他人。

Update1我发现我需要在表单登录配置中添加选项(总是-使用-默认-目标="true")。这是必要的,因为在AJAX请求被重定向到登录页面后(由于会话过期),Spring会记住之前的AJAX请求并在登录后自动重定向到它。这导致返回的JSON显示在浏览器页面上。当然,不是我想要的。

Update2而不是使用always-use-default-target="true",使用@RobWink阻止来自请求缓存的AJAX请求的示例。这允许正常链接在登录后重定向到其原始目标,但AJAX在登录后转到主页。

让我再次引用@Steg所描述的问题

我遇到了与您类似的问题。我执行了一个具有2的ajax请求可能的响应:将浏览器重定向到新页面并将当前页面上现有的超文本标记语言表单替换为新表单一。

恕我直言,这是一个真正的挑战,必须正式扩展到当前的HTTP标准。

我相信新的Http标准将使用新的状态代码。含义:当前301/302告诉浏览器去获取这个请求的内容到新的location

在扩展标准中,它会说如果响应status: 308(只是一个例子),那么浏览器应该将主页重定向到提供的location

话虽如此;我倾向于模仿这个未来行为,因此当需要document.redirect时,我让服务器响应为:

status: 204 No Contentx-status: 308 Document Redirectx-location: /login.html

当JS获得“status: 204”时,它会检查x-status: 308标头的存在,并对location标头中提供的页面进行document.redirect。

这对你有意义吗?

使用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')});

后端Spring@ExceptionHandler

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

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

@Order(HIGHEST_PRECEDENCE)public class ExceptionHandlerAdvise {
private static Logger logger = LoggerFactory.getLogger(ExceptionHandlerAdvise.class);
@Autowiredprivate 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();}

登录处理程序拦截器

@Servicepublic class LoginHandlerInterceptor implements HandlerInterceptor {
@Autowiredprivate UserInfo userInfo;
@Overridepublic 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的替代品,正在开发一个新的获取API,它允许手动重定向处理。你需要检查一下如果当前浏览器支持足以满足您的需求。