javascript - запретить - редирект после ajax запроса




Как управлять запросом перенаправления после вызова jQuery Ajax (20)

Большинство из этих решений используют обходной путь, используя дополнительный заголовок или неадекватный HTTP-код. Эти решения, скорее всего, будут работать, но немного «взломать». Я придумал другое решение.

Мы используем WIF, который настроен на перенаправление (passiveRedirectEnabled = "true") на ответ 401. Переадресация полезна при обращении с обычными запросами, но не будет работать для 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;
        }
    }

Это позволяет вам возвращать 401 ответов на запросы AJAX, которые ваш 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);
    }
});

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

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


Другое решение, которое я нашел (особенно полезно, если вы хотите установить глобальное поведение), заключается в использовании $.ajaxsetup() вместе со свойством statusCode . Как указывали другие, не используйте код состояния переадресации ( 3xx ), вместо этого используйте код состояния 4xx и обрабатывайте клиентскую часть перенаправления.

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

Замените 400 кодом состояния, который вы хотите обработать. Как уже упоминалось, 401 Unauthorized может быть хорошей идеей. Я использую 400 поскольку он очень неспецифичен, и я могу использовать 401 для более конкретных случаев (например, неправильные учетные данные). Поэтому вместо перенаправления непосредственно ваш backend должен возвращать код ошибки 4xx когда время ожидания сеанса, и вы обрабатываете клиентскую часть перенаправления. Прекрасно подходит для меня даже с такими фреймворками, как backbone.js


Мне нравится метод Тиммерса с небольшим завихрением лимона. Если вы когда-либо возвращаете 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();
    }

});

Некоторые могут найти ниже полезное:

Я хотел, чтобы клиенты были перенаправлены на страницу входа в систему для любого действия rest, которое отправляется без токена авторизации. Поскольку все мои действия по отдыху основаны на 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);
        }
    };      
});

И это все.


Объединив то, что Владимир Прудников и Томас Хансен сказали:

  • Измените свой серверный код, чтобы определить, является ли это 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); 
});

Позвольте мне просто повторить проблему, описанную @Steg

У меня была схожая проблема с твоей. Я выполняю запрос ajax, который имеет 2 возможных ответа: один, который перенаправляет браузер на новую страницу и заменяет существующую HTML-форму на текущей странице новой.

IMHO - это настоящая проблема, и ее необходимо будет официально распространить на существующие HTTP-стандарты.

Я считаю, что новый стандарт Http будет использовать новый код состояния. значение: в настоящее время 301/302 сообщает браузеру, чтобы он отправил содержимое этого запроса в новое location .

В расширенном стандарте он скажет, что если status: 308 ответа 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 .

Это имеет какое-то значение для вас?


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


У меня есть простое решение, которое работает для меня, не требуется замена кода сервера ... просто добавьте tsp мускатного ореха ...

$(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 для поиска любой уникальной строки на вашей странице входа ...


Эта проблема может возникнуть при использовании метода 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» над фильтром поймает это и оценит, чтобы сделать перенаправление, а не позволять выводить данные.


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

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


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

Решение

Поскольку я не смог захватить 302 ответа на перенаправление, я добавил заголовок LoginPage на моей странице входа, в которой был LoginPage URL-адрес самой страницы входа. В модуле я теперь слушаю заголовок и делаю перенаправление:

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

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


Я решил это, поставив следующее на моей странице login.php.

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

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

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

в сервлете вы должны поставить response.setStatus(response.SC_MOVED_PERMANENTLY); для отправки статуса «301» xmlHttp, необходимого для перенаправления ...

и в функции $ .ajax вы не должны использовать функцию .toString() ..., просто

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

проблема в том, что она не очень гибкая, вы не можете решить, куда вы хотите перенаправить ..

перенаправление через сервлеты должно быть лучшим способом. но я все еще не могу найти правильный способ сделать это.


Если вы также хотите передать значения, вы также можете установить переменные сеанса и получить доступ. Например: в вашем jsp вы можете написать

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

И тогда вы можете сохранить это значение temp в своей переменной javascript и поиграть


Кроме того, вы, вероятно, захотите перенаправить пользователя на указанный в URL-адрес заголовков. Итак, это будет выглядеть так:

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

UPD: Opps. Иметь ту же задачу, но она не работает. Делать это. Я покажу вам решение, когда найду его.


Наконец, я решаю проблему, добавляя пользовательский интерфейс HTTP Header. Как раз перед ответом для каждого запроса на стороне сервера, я добавляю текущий запрошенный url в заголовок ответа.

Мой тип приложения на сервере есть Asp.Net MVC, и у него есть хорошее место для этого. в Global.asaxi реализовано 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....

и еще одна важная вещь заключается в том, что нет необходимости изменять код на стороне сервера или на стороне клиента.

а следующая - возможность получить доступ к другой информации о post action таких ошибках, сообщениях и ..., таким образом.

Я разместил это, может быть, помог кому-то :)


Я не имел никакого успеха в решении заголовка - они никогда не были взяты в моем методе ajaxSuccess / ajaxComplete. Я использовал ответ Штега с пользовательским ответом, но я немного изменил сторону 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;
}

Я получил рабочий solulion , используя ответы из @John и @Arpad .com/a/8426947/4505142 и @RobWinch link

Я использую Spring Security 3.2.9 и jQuery 1.10.2.

Расширьте класс Spring, чтобы вызвать ответ 4XX только из запросов AJAX:

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>

В моих JSP добавьте глобальный обработчик ошибок AJAX, как показано 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");
          }
      }
  });

Кроме того, удалите существующие обработчики ошибок из вызовов AJAX в JSP-страницах:

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

Надеюсь, это поможет другим.

Update1 Я обнаружил, что мне нужно добавить параметр (always-use-default-target = "true") в конфигурацию для входа в форму. Это было необходимо, так как после запроса AJAX перенаправляется на страницу входа (из-за истекшего сеанса), Spring запоминает предыдущий запрос AJAX и автоматически перенаправляет его после входа в систему. Это приведет к отображению возвращаемого JSON на странице браузера. Конечно, не то, что я хочу.

Update2 Вместо использования always-use-default-target="true"используйте пример @RobWinch для блокировки запросов AJAX из requstCache. Это позволяет нормальным ссылкам перенаправляться на исходную цель после входа в систему, но AJAX переходит на домашнюю страницу после входа в систему.





redirect