Как управлять запросом на перенаправление после вызова jQuery Ajax - PullRequest
1276 голосов
/ 14 октября 2008

Я использую $.post() для вызова сервлета с помощью Ajax, а затем использую полученный фрагмент HTML для замены элемента div на текущей странице пользователя. Однако, если время сеанса истекло, сервер отправляет директиву перенаправления, чтобы отправить пользователя на страницу входа. В этом случае jQuery заменяет элемент div содержимым страницы входа в систему, заставляя пользователя увидеть действительно редкую сцену.

Как мне управлять директивой перенаправления из вызова Ajax с помощью jQuery 1.2.6?

Ответы [ 31 ]

668 голосов
/ 08 октября 2009

Я прочитал этот вопрос и реализовал подход, который был заявлен в отношении установки кода состояния ответа на 278 во избежание прозрачной обработки браузером перенаправлений. Несмотря на то, что это сработало, я был немного недоволен, так как это немного хакерство.

После дальнейших поисков я отказался от этого подхода и использовал JSON . В этом случае все ответы на ajax-запросы имеют код состояния 200, а тело ответа содержит объект JSON, созданный на сервере. Затем JavaScript на клиенте может использовать объект JSON, чтобы решить, что ему нужно делать.

У меня была проблема, похожая на вашу. Я выполняю запрос ajax, который имеет 2 возможных ответа: один перенаправляет браузер на новую страницу, а другой заменяет существующую 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 "данные" создается на сервере, чтобы иметь 2 члена: data.redirect и data.form. Я нашел этот подход намного лучше.

226 голосов
/ 24 февраля 2009

Я решил эту проблему следующим образом:

  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 = '/';
        }
    });
    
110 голосов
/ 20 ноября 2008

Ни один браузер не обрабатывает правильно ответы 301 и 302. И фактически в стандарте даже говорится, что они должны обращаться с ними «прозрачно», что является БОЛЬШОЙ головной болью для поставщиков библиотек Ajax. В Ra-Ajax мы были вынуждены использовать код состояния ответа HTTP 278 (просто некоторый «неиспользуемый» код успеха) для прозрачной обработки перенаправлений с сервера ...

Это действительно раздражает меня, и если у кого-то здесь есть некоторая «тяга» в W3C, я был бы признателен, если бы вы дали W3C знать , что нам действительно нужно обрабатывать коды 301 и 302 самим ...! ;)

89 голосов
/ 27 января 2009

Решение, которое в конечном итоге было реализовано, состояло в том, чтобы использовать оболочку для функции обратного вызова вызова 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 всегда возвращали HTML внутри элемента DIV, который мы используем для замены части страницы. Кроме того, нам нужно было только перенаправить на страницу входа.

60 голосов
/ 23 августа 2011

Мне нравится метод Тиммерца с легким поворотом лимона. Если вам когда-либо будет возвращено contentType из text / html , когда вы ожидаете JSON , скорее всего, вас перенаправят. В моем случае я просто перезагружаю страницу, и она перенаправляется на страницу входа. О, и проверьте, что статус 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 page
        window.location.reload();
    }

});
46 голосов
/ 14 октября 2008

Используйте низкоуровневый $.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';
}
32 голосов
/ 23 мая 2012

Я просто хотел поделиться своим подходом, так как это может кому-то помочь:

Я в основном включил модуль 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);
    }
}

... и это работает как шарм :). Вы можете удивиться, почему я включил URL в заголовок LoginPage ... в основном потому, что я не нашел способа определить URL-адрес GET в результате автоматического перенаправления местоположения из объекта xhr ...

28 голосов
/ 07 мая 2013

Я знаю, что эта тема старая, но я дам еще один подход, который я нашел и ранее описал здесь . В основном я использую ASP.MVC с WIF (но это не очень важно для контекста этой темы - ответ адекватен, независимо от того, какие платформы используются. Ключ остается неизменным - решение проблем, связанных со сбоями аутентификации, в то время как выполнение запросов ajax) .

Подход, показанный ниже, может быть применен ко всем запросам 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() {}

Если пользователь не аутентифицирован (токен истек), такой метод недоступен (защищен атрибутом 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;
                }
            }
    });
}
22 голосов
/ 06 января 2012

Я решил эту проблему следующим образом:

Добавьте промежуточное программное обеспечение для обработки ответа, если это перенаправление для запроса 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;
       }
   }
}
22 голосов
/ 23 октября 2012

Я думаю, что лучший способ справиться с этим - использовать существующие коды ответов протокола 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, это более общий термин, и вы не пишете какую-то новую пользовательскую спецификацию / заголовок. Вам также не нужно изменять какие-либо из ваших существующих вызовов ajax.

Редактировать: Согласно приведенному ниже комментарию @ Роба, 401 (код состояния HTTP для ошибок аутентификации) должен быть индикатором. См. 403 Запрещено против 401 Несанкционированных ответов HTTP для получения более подробной информации. При этом некоторые веб-фреймворки используют 403 как для аутентификации, так и для ошибок авторизации - поэтому адаптируйтесь соответственно. Спасибо, Роб.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...