我正在使用$.post()
Ajax调用servlet,然后使用生成的HTML片段替换div
用户当前页面中的元素.但是,如果会话超时,服务器会发送重定向指令以将用户发送到登录页面.在这种情况下,jQuery正在用div
登录页面的内容替换元素,迫使用户的目光确实见证了罕见的场景.
如何使用jQuery 1.2.6从Ajax调用管理重定向指令?
我阅读了这个问题并实现了关于将响应状态代码设置为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"在服务器上构造为具有2个成员:data.redirect和data.form.我发现这种方法要好得多.
我解决了这个问题:
向响应添加自定义标头:
public ActionResult Index(){ if (!HttpContext.User.Identity.IsAuthenticated) { HttpContext.Response.AddHeader("REQUIRES_AUTH","1"); } return View(); }
将JavaScript函数绑定到ajaxSuccess
事件并检查标头是否存在:
$(document).ajaxSuccess(function(event, request, settings) { if (request.getResponseHeader('REQUIRES_AUTH') === '1') { window.location = '/'; } });
没有浏览器正确处理301和302响应.事实上,该标准甚至表示他们应该"透明地"处理它们,这对于Ajax Library供应商来说是一个非常令人头痛的问题.在Ra-Ajax中,我们被迫使用HTTP响应状态代码278(只是一些"未使用的"成功代码)来处理来自服务器的透明重定向...
这真让我烦恼,如果有人在W3C中有一些"拉",我会很高兴你能让W3C 知道我们真的需要自己处理301和302代码......!;)
最终实现的解决方案是使用包装器来执行Ajax调用的回调函数,并在此包装器中检查返回的HTML块上是否存在特定元素.如果找到该元素,则包装器执行重定向.如果没有,则包装器将调用转发给实际的回调函数.
例如,我们的包装函数类似于:
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.此外,我们只需要重定向到登录页面.
我喜欢Timmerz的方法,略带柠檬味.如果你有机会返回的contentType中的text/html,当你期待JSON,你是最有可能被重定向.在我的情况下,我只是重新加载页面,它被重定向到登录页面.哦,并检查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(); } });
使用低级别$.ajax()
电话:
$.ajax({ url: "/yourservlet", data: { }, complete: function(xmlHttp) { // xmlHttp is a XMLHttpRquest object alert(xmlHttp.status); } });
试试这个重定向:
if (xmlHttp.code != 200) { top.location.href = '/some/other/page'; }
我只是想分享我的方法,因为这可能会对某人有所帮助:
我基本上包括一个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
从xhr
对象的自动位置重定向产生的url ...
我知道这个话题很老,但我会给出另一种方法,我已经找到并在此处描述过.基本上我正在使用带有WIF的ASP.MVC (但这对于本主题的上下文并不重要 - 无论使用哪个框架,答案都是足够的.线索保持不变 - 在执行ajax请求时处理与身份验证失败相关的问题).
下面显示的方法可以应用于开箱即用的所有ajax请求(如果他们没有在发送事件之前重新定义).
$.ajaxSetup({ beforeSend: checkPulse, error: function (XMLHttpRequest, textStatus, errorThrown) { document.open(); document.write(XMLHttpRequest.responseText); document.close(); } });
在执行任何ajax请求之前,CheckPulse
调用方法(控制器方法可以是最简单的):
[Authorize] public virtual void CheckPulse() {}
如果用户未经过身份验证(令牌已过期),则无法访问此类方法(受Authorize
属性保护).因为框架处理身份验证,所以当令牌过期时,它会将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"); } }
最后在客户端检查这样的自定义标头.如果存在 - 应该完成到登录页面的完全重定向(在我的情况下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 execution window.location = location; } } }); }
我这样解决了这个问题:
添加中间件以处理响应,如果它是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协议响应代码401 Unauthorized
.
这是我解决它的方式:
服务器端:如果会话到期,请求是ajax.发送401响应代码头
客户端:绑定到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这是更通用的,你不是在写一些新的自定义规范/标题.您也不必修改任何现有的ajax调用.
编辑: Per @ Rob的评论如下,401(认证错误的HTTP状态代码)应该是指标.有关更多详细信息,请参阅403 Forbidden vs 401 Unauthorized HTTP响应.有人说,一些Web框架使用403进行身份验证和授权错误 - 因此相应地进行调整.谢谢Rob.
大多数给定的解决方案使用解决方法,使用额外的标头或不适当的HTTP代码.这些解决方案很可能会起作用,但感觉有点'hacky'.我想出了另一个解决方案.
我们正在使用配置为在401响应上重定向(passiveRedirectEnabled ="true")的WIF.重定向在处理正常请求时很有用,但不适用于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将用户重定向到登录页面).
一个示例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); } });
我发现的另一个解决方案(如果要设置全局行为,尤其有用)是将该$.ajaxsetup()
方法与statusCode
属性一起使用.像其他人指出的那样,不要使用重定向statuscode(3xx
),而是使用状态代码4xx
并处理重定向客户端.
$.ajaxSetup({ statusCode : { 400 : function () { window.location = "/"; } } });
替换400
为您要处理的状态代码.就像已经提到的那样401 Unauthorized
可能是一个好主意.我使用400
它,因为它非常不明确,我可以使用401
更具体的情况(如错误的登录凭据).因此,4xx
当会话超时并且您处理重定向客户端时,您的后端应该返回错误代码,而不是直接重定向.即使使用像backbone.js这样的框架,也适合我
然后可能会出现此问题使用ASP.NET MVC RedirectToAction方法.为了防止表单在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; } });
如果数据为:"window.location ='/ Acount/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( "" ) > -1 ) window.location = window.location; else intercepted_success( a, b, c ); }; }); });
我检查是否存在html标记,但您可以更改indexOf以搜索登录页面中存在的任何唯一字符串...
将弗拉基米尔·普鲁德尼科夫和托马斯·汉森所说的放在一起:
更改服务器端代码以检测它是否为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); });
尝试
$(document).ready(function () { if ($("#site").length > 0) { window.location = "<%= Url.Content("~") %>" + "Login/LogOn"; } });
把它放在登录页面上.如果它被加载到主页面上的div中,它将重定向直到登录页面."#site"是div的id,它位于除登录页面之外的所有页面上.
虽然答案似乎适用于人们,如果您使用的是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
我通过在login.php页面中添加以下内容解决了这个问题.
让我再次引用@Steg描述的问题
我遇到了类似的问题.我执行一个有两个可能响应的ajax请求:一个将浏览器重定向到一个新页面,另一个用新的替换当前页面上的现有HTML表单.
恕我直言,这是一个真正的挑战,必须正式扩展到当前的HTTP标准.
我相信新的Http标准将使用新的状态代码.意思是:目前301/302
告诉浏览器去读取的内容这个请求到一个新的location
.
在扩展标准中,它会说如果响应status: 308
(只是一个例子),那么浏览器应该将主页面重定向到location
提供的.
话虽如此; 我倾向于已经模仿这种未来行为,因此当需要document.redirect时,我让服务器响应为:
status: 204 No Content x-status: 308 Document Redirect x-location: /login.html
当JS获取" status: 204
"时,它会检查x-status: 308
标头是否存在,并对标题中提供的页面执行document.redirect location
.
这对你有意义吗?
有些人可能会发现以下有用:
我希望将客户端重定向到登录页面,以便在没有授权令牌的情况下发送任何休息操作.由于我的所有rest-actions都是基于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 { Mapcookies = 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);
中发送重定向所需的'301'xmlHttp状态...
并且在$ .ajax函数中你不应该使用.toString()
函数....
if (xmlHttp.status == 301) {
top.location.href = 'xxxx.jsp';
}
问题是它不是很灵活,你无法决定你想要重定向的位置..
重定向通过servlet应该是最好的方法.但我仍然无法找到正确的方法.
我只想锁定整个页面的任何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"); }
如果您还想传递值,那么您还可以设置会话变量并访问例如:在您的jsp中,您可以编写
<% HttpSession ses = request.getSession(true); String temp=request.getAttribute("what_you_defined"); %>
然后你可以将这个临时值存储在你的javascript变量中并进行游戏
我没有使用标头解决方案取得任何成功 - 它们从未在我的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添加到响应的头部.
我在服务器上的应用程序类型是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
的状态的方法302
,303
.......
其他重要的是没有必要修改服务器端和客户端的代码.
接下来就是能够以这种方式访问后期行动的其他信息,例如错误,消息和......
我贴了这个,也许帮助别人:)