javascript - ajax后台重定向 - ajax重定向




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

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.

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

我如何使用jQuery 1.2.6从Ajax调用中管理重定向指令?


I got a working solulion using the answers from @John and @Arpad .com/a/8426947/4505142 and @RobWinch link

I use Spring Security 3.2.9 and jQuery 1.10.2.

Extend Spring's class to cause 4XX response only from AJAX requests:

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>

In my JSPs, add a global AJAX error handler as shown here

  $( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
      if ( jqxhr.status === 403 ) {
          window.location = "login.jsp";
      } else {
          if(thrownError != null) {
              alert(thrownError);
          } else {
              alert("error");
          }
      }
  });

Also, remove existing error handlers from AJAX calls in JSP pages:

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

I hope it helps others.

Update1 I found that I needed to add the option (always-use-default-target="true") to the form-login config. This was needed since after an AJAX request gets redirected to the login page (due to expired session), Spring remembers the previous AJAX request and auto redirects to it after login. This causes the returned JSON to be displayed on the browser page. Of course, not what I want.

Update2 Instead of using always-use-default-target="true" , use @RobWinch example of blocking AJAX requests from the requstCache. This allows normal links to be redirected to their original target after login, but AJAX go to the home page after login.


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

使用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'”,则上面的过滤器会捕获该数据并评估以进行重定向,而不是让数据显示。


在servlet中你应该放置response.setStatus(response.SC_MOVED_PERMANENTLY); 发送重定向所需的'301'xmlHttp状态...

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

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

问题是它不是很灵活,你不能决定你想要重定向的位置。

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


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

我们使用配置为在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);
    }
});

将弗拉基米尔普鲁德尼科夫和托马斯汉森的观点放在一起:

  • 更改您的服务器端代码以检测它是否是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。


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

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

我基本上包括一个JavaScript模块,用于处理认证内容,如显示用户名,以及处理重定向到登录页面的情况

我的场景:我们之间基本上有一个ISA服务器,它监听所有请求, 在我们的登录页面使用302和位置标题进行响应

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

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

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

解决方案

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

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

...而且它的作用像魅力:)。 您可能想知道为什么我将url包含在LoginPage标题中......基本上,因为我没有发现确定从xhr对象的自动位置重定向导致的GET url的方法...


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

$(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以搜索登录页面中存在的任何唯一字符串...


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

我解决了这个问题:

  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 = '/';
        }
    });
    

我解决了这个问题:

添加一个中间件来处理响应,如果它是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;
      }

    }

我读过这个问题,并实现了将响应状态代码设置为278的方法,以避免浏览器透明地处理重定向。 尽管这有效,但我有点不满意,因为它有点破解。

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

我遇到了类似的问题。 我执行一个包含2个可能响应的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。 我发现这种方法要好得多。


我通过在login.php页面中添加以下内容来解决此问题。

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

最终实现的解决方案是为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。 另外,我们只需要重定向到登录页面。


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

我希望客户端被重定向到登录页面,以便发送没有授权令牌的任何休息操作。 由于我的所有休息操作都是基于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);
        }
    };      
});

这就是它。


虽然答案似乎适用于人们,如果你使用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"));
}

}

来源: 2


这对我有用:

success: function(data, textStatus, xhr) {

        console.log(xhr.status);
}

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





redirect