关于javascript:如何在jquery ajax调用后管理重定向请求

How to manage a redirect request after a jQuery Ajax call

我使用$.post()调用servlet,使用ajax,然后使用生成的HTML片段替换用户当前页面中的div元素。但是,如果会话超时,服务器会发送重定向指令,将用户发送到登录页面。在这种情况下,jquery将div元素替换为登录页面的内容,从而迫使用户亲眼目睹一个罕见的场景。

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


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

在深入研究之后,我放弃了这种方法并使用了JSON。在这种情况下,对Ajax请求的所有响应都具有状态代码200,响应主体包含一个在服务器上构建的JSON对象。然后,客户机上的JavaScript可以使用JSON对象来决定它需要做什么。

我有一个和你类似的问题。我执行的Ajax请求有两个可能的响应:一个将浏览器重定向到新页面,另一个将当前页面上现有的HTML表单替换为新页面。要执行此操作的jquery代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$.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。我发现这种方法好多了。


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

  • 向响应添加自定义头:

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

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

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

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


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

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

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

    然后,在进行Ajax调用时,我们使用了如下方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $.post("myAjaxHandler",
           {
            param1: foo,
            param2: bar
           },
           function(data){
               cbWrapper(data, myActualCB);
           },
          "html"
    );

    这对我们很有用,因为所有的Ajax调用总是返回DIV元素内的HTML,我们使用它来替换页面的一部分。另外,我们只需要重定向到登录页面。


    我喜欢Timmerz的方法加上一点柠檬。如果在期望JSON时收到ContentType的text/html返回,则很可能会被重定向。在我的例子中,我只需重新加载页面,它就会被重定向到登录页面。哦,检查一下jqxhr的状态是200,这看起来很傻,因为您在错误函数中,对吧?否则,合法的错误案例将强制进行迭代重新加载(oops)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $.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()呼叫:

    1
    2
    3
    4
    5
    6
    7
    8
    $.ajax({
      url:"/yourservlet",
      data: { },
      complete: function(xmlHttp) {
        // xmlHttp is a XMLHttpRquest object
        alert(xmlHttp.status);
      }
    });

    尝试此重定向:

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


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

    我基本上包括了一个javascript模块,它处理诸如显示用户名之类的身份验证工作,以及这个案例处理到登录页面的重定向。

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

    在我的javascript模块中,我的初始方法是

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

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

    解决方案

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

    1
    2
    3
    4
    5
    6
    if(xhr.status === 200){
        var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
        if(loginPageRedirectHeader && loginPageRedirectHeader !==""){
            window.location.replace(loginPageRedirectHeader);
        }
    }

    …这就像魅力一样。你可能想知道我为什么要在LoginPage头中包含URL…基本上是因为我找不到确定GET的URL的方法,这是由于来自xhr对象的自动位置重定向造成的…


    我知道这个主题已经过时了,但我将给出另一种方法,我已经找到并在这里描述过了。基本上,我正在将ASP.MVC与WIF结合使用(但这对本主题的上下文并不重要-无论使用哪种框架,答案都是充分的。线索保持不变-在执行Ajax请求时处理与身份验证失败相关的问题)。

    下面所示的方法可以应用于所有现成的Ajax请求(如果它们没有明显地重新定义beforesend事件)。

    1
    2
    3
    4
    5
    6
    7
    8
    $.ajaxSetup({
        beforeSend: checkPulse,
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            document.open();
            document.write(XMLHttpRequest.responseText);
            document.close();
        }
    });

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

    1
    2
    [Authorize]
    public virtual void CheckPulse() {}

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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替换,请求由我的框架自动处理)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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将响应更改为正常响应。

    1
    2
    3
    4
    5
    6
    7
    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中,如果响应包含重定向,则必须是重定向,因此请更改浏览器的位置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $('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事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $('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调用。

    编辑:根据下面@rob的评论,401(验证错误的HTTP状态代码)应该是指示器。有关详细信息,请参见403 Forbidden vs 401 Unauthorized HTTP Responses。也就是说,一些Web框架使用403来处理身份验证和授权错误——因此要相应地进行调整。谢谢Rob。


    大多数给定的解决方案都使用一个变通方法,使用一个额外的头或一个不适当的HTTP代码。这些解决方案很可能会奏效,但感觉有点"黑客"。我想出了另一个解决办法。

    我们使用的WIF配置为在401响应上重定向(passiveredDirectEnabled="true")。重定向在处理普通请求时很有用,但对Ajax请求无效(因为浏览器不会执行302/Redirect)。

    使用global.asax中的以下代码,可以禁用Ajax请求的重定向:

    1
    2
    3
    4
    5
    6
    7
    8
    9
        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会将用户重定向到登录页面)。

    处理401错误的示例javascript:

    1
    2
    3
    4
    5
    6
    7
    $(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属性一起使用。像其他人指出的那样,不要使用重定向状态码(3xx),而是使用4xx状态码并处理重定向客户端。

    1
    2
    3
    4
    5
    6
    7
    $.ajaxSetup({
      statusCode : {
        400 : function () {
          window.location ="/";
        }
      }
    });

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


    然后使用ASP.NET MVC RedirectToAction方法可能会出现此问题。为了防止表单在DIV中显示响应,您可以简单地使用某种Ajax响应过滤器将响应输入到$.AjaxSetup中。如果响应包含MVC重定向,则可以在JS端评估此表达式。下面是JS的示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $.ajaxSetup({
        dataFilter: function (data, type) {
            if (data && typeof data =="string") {
                if (data.indexOf('window.location') > -1) {
                    eval(data);
                }
            }
            return data;
        }
    });

    如果数据为:"window.location='/a帐户/登录'",则上面的筛选器将捕获该数据并进行计算以进行重定向,而不是让数据显示出来。


    把弗拉基米尔·普鲁德尼科夫和托马斯·汉森的话综合起来:

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

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

    • 在JS中,确保表单提交是通过Ajax提交的,检查响应代码并在需要时重定向:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $('#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);
    });


    我有一个简单的解决方案,对我有效,不需要更改服务器代码…只需添加一汤匙肉豆蔻…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $(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以搜索登录页中存在的任何唯一字符串…


    尝试

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

    把它放在登录页面上。如果它被加载到主页面上的一个DIV中,它将重定向到登录页。"#站点"是一个DIV的ID,它位于除登录页面之外的所有页面上。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        function showValues() {
            var str = $("form").serialize();
            $.post('loginUser.html',
            str,
            function(responseText, responseStatus, responseXML){
                if(responseStatus=="success"){
                    window.location="adminIndex.html";
                }
            });    
        }

    我通过在login.php页面中放置以下内容来解决这个问题。

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

    如果您使用的是Spring安全性,那么答案似乎对人们有用,但是我发现扩展了LoginLauthenAuthenticationEntryPoint,并添加了特定的代码来处理Ajax,使其更加健壮。大多数示例截获所有重定向,而不仅仅是身份验证失败。这对我从事的项目来说是不可取的。如果不希望缓存失败的Ajax请求,您可能会发现还需要扩展exceptiontranslationfilter并重写"sendstartauthentication"方法以删除缓存步骤。

    示例AjaxawareAuthenticationEntryPoint:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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


    我再引用一下@steg所描述的问题。

    I had a similar problem to yours. I perform an ajax request that has 2
    possible responses: one that redirects the browser to a new page and
    one that replaces an existing HTML form on the current page with a new
    one.

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

    我相信新的HTTP标准将使用新的状态代码。意思:目前301/302通知浏览器去把这个请求的内容提取到一个新的location中。

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

    也就是说,我倾向于模仿未来的行为,因此当需要document.redirect时,服务器的响应如下:

    1
    2
    3
    status: 204 No Content
    x-status: 308 Document Redirect
    x-location: /login.html

    当JS得到"status: 204时,它检查x-status: 308头是否存在,并执行document.redirect到location头中提供的页面。

    这对你有意义吗?


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

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

    这就是我所做的:

    在任何Ajax请求上,我的服务器都会返回JSON200响应"需要认证"(如果客户机需要认证)。

    Java(服务器端)的简单示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @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中,我添加了以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $.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);
            }
        };      
    });

    这就是问题所在。


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

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

    然后你可以把这个温度值存储在你的javascript变量中,然后四处游玩。


    我没有任何成功的头部解决方案-他们从来没有在我的AjaxSuccess/AjaxComplete方法。我在自定义响应中使用了steg的答案,但是我修改了一些JS端。我在每个函数中设置了一个调用的方法,以便使用标准的$.get$.post方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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);
        }
    }

    使用中的示例…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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;
    }

    我只想锁定整个页面的任何Ajax请求。@Superg让我开始了。以下是我的结论:

    1
    2
    3
    4
    5
    6
    7
    8
    // 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个?)我本可以写:

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


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

    我在服务器上的应用程序类型是Asp.Net MVC,它有一个很好的位置来完成它。在Global.asax中,我实施了Application_EndRequest事件,因此:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        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产生的,…。.

    另一件重要的事情是,不需要在服务器端或客户机端修改代码。

    下一个功能是以这种方式访问操作后的其他信息,如错误、消息和…。

    我贴了这个,也许能帮助别人:)


    你应该把response.setStatus(response.SC_MOVED_PERMANENTLY);要发送重定向所需的"301"xmlhttp状态…

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

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

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

    通过servlet重定向应该是最好的方法。但我还是找不到正确的方法。


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

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

    然后我使用jquery.ajax在客户机上发送/处理请求,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <button onclick="*the-jquery*">Delete</button>

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

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

    附言:这是一个巨大的痛苦,编辑在移动版的SO。愚蠢的ISP把我的服务取消请求通过权利当我完成我的答案!


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

    我编写这段代码是为了处理一个500页的过期错误,但它也可以捕获一个200页的重定向。准备好xmlhttprequest on readystate上的维基百科条目更改readystate的含义。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 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);
    }

    我用@john和@arpad link以及@robwinch link的答案得到了一个有效的解。

    我使用SpringSecurity 3.2.9和jQuery1.10.2。

    扩展Spring的类以仅从Ajax请求引起4xx响应:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
      <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错误处理程序,如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
      $( 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调用中删除现有的错误处理程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
            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不要使用always-use-default-target="true",而是使用@robwing示例阻止来自RequistCache的Ajax请求。这允许在登录后将普通链接重定向到其原始目标,但Ajax在登录后转到主页。


    此外,您可能希望将用户重定向到给定的in headers url。最后看起来是这样的:

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

    UPD:OPPS。有相同的任务,但不起作用。做这些事。当我找到答案时,我会给你看。


    这对我很有用:

    1
    2
    3
    4
    success: function(data, textStatus, xhr) {

            console.log(xhr.status);
    }

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