如何在jQuery Ajax调用之后pipe理redirect请求

我使用$.post()来调用使用Ajax的servlet,然后使用生成的HTML片段replace用户当前页面中的div元素。 但是,如果会话超时,服务器将发送redirect指令以将用户发送到login页面。 在这种情况下,jQuery正在用login页面的内容replacediv元素,强迫用户的眼睛确实见证一个罕见的场景。

我如何使用jQuery 1.2.6从Ajax调用pipe理redirect指令?

我阅读了这个问题,并实现了将响应状态码设置为278的方法,以避免浏览器透明地处理redirect。 即使这个工作,我有点不满,因为这是一个黑客。

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

我和你有类似的问题。 我执行一个ajax请求,它有两个可能的响应:一个是将浏览器redirect到一个新页面,另一个是用当前页面replace当前页面上现有的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”在服务器上构build,有两个成员:data.redirect和data.form。 我发现这种方法要好得多。

我解决了这个问题:

  1. 将自定义标题添加到响应中:

     public ActionResult Index(){ if (!HttpContext.User.Identity.IsAuthenticated) { HttpContext.Response.AddHeader("REQUIRES_AUTH","1"); } return View(); } 
  2. 将JavaScript函数绑定到ajaxSuccess事件并检查头是否存在:

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

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

这真的让我很烦恼,如果某个人在W3C有一些“拉”,我会感激你可以让W3C 知道我们真的需要自己处理301和302的代码…! ;)

最终实现的解决scheme是为Ajax调用的callback函数使用包装,并在此包装中检查返回的HTML块中是否存在特定的元素。 如果find元素,则包装器执行redirect。 如果不是,包装器将该呼叫转发到实际的callback函数。

例如,我们的包装函数是这样的:

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

然后,在进行Ajax调用时,我们使用了如下的东西:

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

这对我们是有效的,因为所有的Ajax调用总是在我们使用的DIV元素内部返回HTML来replace页面的一部分。 另外,我们只需要redirect到login页面。

我喜欢Timmerz的方法,柠檬稍微扭曲。 如果您在期待JSON时获得了text / html的返回contentType ,则很可能会被redirect。 在我的情况下,我只是简单地重新加载页面,并将其redirect到login页面。 哦,并检查jqXHR状态是200,这似乎很愚蠢,因为你在错误的function,对不对? 否则,合法的错误情况将强制迭代重新加载(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(); } }); 

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

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

试试这个redirect:

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

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

我基本上包括一个JavaScript模块,处理身份validation的东西,如显示用户名,也处理redirect到login页面

我的场景:我们之间基本上有一个ISA服务器,它监听所有的请求, 在我们的login页面上显示一个302和一个位置标题

在我的JavaScript模块中,我最初的方法是类似的

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

问题(这里已经提到的很多)是,浏览器自己处理redirect,因此我的ajaxCompletecallback从来没有被调用,而是我得到了已经redirect的login页面响应 ,显然是一个status 200 。 问题:你如何检测成功的200响应是你的实际login页面还是其他任意页面?

解决scheme

由于我无法捕获302redirect响应,因此我在login页面上添加了一个LoginPage标题,其中包含login页面本身的URL。 在模块中,我现在听标题并做一个redirect:

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

…这就像魅力:)。 您可能想知道为什么我将url包含在LoginPage标题中…基本上,因为我发现没有办法确定从xhr对象的自动位置redirect导致的GET的url …

我知道这个话题是旧的,但我会给我另一种方法,我已经find和先前在这里描述。 基本上我使用ASP.MVC与WIF (但是这对于这个主题的上下文并不重要 – 无论使用哪个框架,答案都是足够的。线索保持不变 – 在执行Ajax请求时处理与authentication失败相关的问题)

下面显示的方法可以应用于所有的开箱即用的Ajax请求(如果他们不明显重新定义beforeSend事件)。

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

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

 [Authorize] public virtual void CheckPulse() {} 

如果用户未通过身份validation(令牌已过期),则无法访问此类方法(由Authorize属性保护)。 由于框架处理authentication,而令牌到期,它将HTTP状态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"); } } 

最后在客户端检查这样的自定义标题。 如果存在的话 – 完全redirect到login页面应该完成(在我的情况下, window.location被由我的框架自动处理的请求中的urlreplace)。

 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 execution window.location = location; } } }); } 

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

这是我如何解决它:

  1. 服务器端:如果会话过期,并且请求是ajax。 发送一个401响应docker
  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'; } }); 

海事组织这是更通用的,你不写一些新的自定义规范/标题。 你也不应该修改任何你现有的ajax调用。

编辑:下面的 Per @ Rob的评论,401(authentication错误的HTTP状态代码)应该是指标。 有关更多详细信息,请参阅403 Forbidden与401 Unauthorized HTTP响应 。 有人说,一些Web框架使用403authentication和授权错误 – 所以适应相应。 感谢Rob。

我发现的另一个解决scheme(特别是如果你想设置一个全局行为有用)是使用$.ajaxsetup()方法与statusCode属性 。 像其他人一样指出,不要使用redirect状态码( 3xx ),而是使用4xx并处理redirect客户端。

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

400replace为您要处理的状态码。 就像已经提到的401 Unauthorized可能是一个好主意。 我使用400因为它是非常不明确的,我可以使用401更具体的情况(如错误的login凭据)。 因此,当会话超时并且您处理redirect客户端时,不要直接redirect您的后端应返回4xx错误代码。 即使有像backbone.js这样的框架,对我来说也是完美的

使用ASP.NET MVC RedirectToAction方法可能会出现此问题。 为了防止窗体显示在div中的响应,你可以简单地做一些Ajax响应filter来input$ .ajaxSetup响应。 如果响应包含MVCredirect,则可以在JS端评估此expression式。 下面是JS的代码示例:

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

如果数据是: “window.location ='/ Acount / Login'”,那么上面的filter会捕获该数据,并进行redirect而不是让数据显示。

我解决这个问题是这样的:

添加一个中间件来处理响应,如果它是一个ajax请求的redirect,请将响应更改为具有redirecturl的正常响应。

 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中,如果响应包含redirect,它必须是redirect,所以更改浏览器的位置。

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

把弗拉基米尔·普鲁德尼科夫和托马斯·汉森说成是:

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

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

  • 在你的JS中,确保表单提交是通过Ajax,检查响应代码并在需要时redirect:
 $('#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); }); 

大多数给定的解决scheme使用一个解决方法,使用额外的头或不正确的HTTP代码。 这些解决scheme很可能工作,但感觉有点“哈克”。 我想出了另一个解决scheme。

我们使用configuration为在401响应中redirect(passiveRedirectEnabled =“true”)的WIF。 处理正常请求时,redirect是有用的,但不适用于AJAX请求(因为浏览器不会执行302 /redirect)。

在global.asax中使用以下代码,可以禁用AJAX请求的redirect:

  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; } } 

这可以让你返回401响应的AJAX请求,你的JavaScript可以通过重新加载页面来处理。 重新加载页面将会抛出一个401将被WIF处理(WIF将把用户redirect到login页面)。

一个JavaScript处理401错误的例子:

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

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

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

我检查是否存在html标签,但是您可以更改indexOf来searchlogin页面中存在的唯一string。

尝试

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

把它放在login页面上。 如果它被加载到主页面上的一个div上,它将redirect到login页面。 “#site”是位于除login页面以外的所有页面上的div的id。

  <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> 

虽然答案似乎适用于人,如果您使用Spring Security我已经发现扩展LoginUrlAuthenticationEntryPoint和添加特定的代码来处理AJAX更强大。 大部分示例拦截所有redirect,而不仅仅是身份validation失败。 这对我所从事的项目来说是不可取的。 如果不希望caching失败的AJAX请求,您可能还需要扩展ExceptionTranslationFilter并覆盖“sendStartAuthentication”方法以删除caching步骤。

示例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

有些人可能会发现以下有用的:

我希望客户端被redirect到没有授权令牌的任何rest操作的login页面。 由于我的所有rest-actions都是基于Ajax的,所以我需要一个很好的通用方法来redirect到login页面,而不是处理Ajax成功函数。

这就是我所做的:

在任何Ajax请求我的服务器将返回一个JSON 200响应“需要authentication”(如果客户端需要authentication)。

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); } }; }); 

这就是它。

在servlet中你应该把response.setStatus(response.SC_MOVED_PERMANENTLY); 发送您需要redirect的'301'xmlHttp状态…

在$ .ajax函数中,你不应该使用.toString()函数

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

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

通过servletredirect应该是最好的方法。 但我仍然无法find正确的方法来做到这一点。

如果你也想传递值,那么你也可以设置会话variables和访问例如:在你的jsp中,你可以写

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

然后,你可以将这个临时值存储在你的javascriptvariables中,然后玩

我没有任何成功的头解决scheme – 他们从来没有拿起我的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; } 

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

我的服务器上的应用程序types是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以及其他响应标头,这些响应标头是由状态303 ,…的POST方法引起的。

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

而下一个就是能够获得后置动作的其他信息,比如错误,消息,…这样。

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

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

I was having this problem on a django app I'm tinkering with (disclaimer: I'm tinkering to learn, and am in no way an expert). What I wanted to do was use jQuery ajax to send a DELETE request to a resource, delete it on the server side, then send a redirect back to (basically) the homepage. When I sent HttpResponseRedirect('/the-redirect/') from the python script, jQuery's ajax method was receiving 200 instead of 302. So, what I did was to send a response of 300 with:

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

Then I sent/handled the request on the client with jQuery.ajax like so:

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

Maybe using 300 isn't "right", but at least it worked just like I wanted it to.

PS :this was a huge pain to edit on the mobile version of SO. Stupid ISP put my service cancellation request through right when I was done with my answer!

You can also hook XMLHttpRequest send prototype. This will work for all sends (jQuery/dojo/etc) with one handler.

I wrote this code to handle a 500 page expired error, but it should work just as well to trap a 200 redirect. Ready the wikipedia entry on XMLHttpRequest onreadystatechange about the meaning of readyState.

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

Additionally you will probably want to redirect user to the given in headers URL. So finally it will looks like this:

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

UPD: Opps. Have the same task, but it not works. Doing this stuff. I'll show you solution when I'll find it.

这对我工作:

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

on success, ajax will get the same status code the browser gets from the server and execute it.