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

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

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

34 ответа

Решение

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

Я прочитал этот вопрос и реализовал подход, который был заявлен в отношении установки кода состояния ответа на 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. Я нашел этот подход гораздо лучше.

Я решил эту проблему путем:

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

Ни один браузер не обрабатывает ответы 301 и 302 правильно. И на самом деле в стандарте даже говорится, что они должны обращаться с ними "прозрачно", что является огромной головной болью для поставщиков Ajax Library. В Ra-Ajax мы были вынуждены использовать код статуса ответа HTTP 278 (просто некоторый "неиспользованный" код успеха) для прозрачной обработки перенаправлений с сервера...

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

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

});

Используйте низкий уровень $.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);
    }
}

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

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

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

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

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

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

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

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

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

Большинство данных решений используют обходной путь, используя дополнительный заголовок или неадекватный 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);
    }
});

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

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

Эта проблема может возникнуть при использовании метода RedirectToAction ASP.NET MVC. Чтобы форма не отображала ответ в 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;
    }
});

Если data: "window.location = '/ Acount / Login'", то фильтр выше поймает это и выполнит перенаправление вместо того, чтобы позволить отображению данных.

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

  • Измените свой код на стороне сервера, чтобы определить, является ли он 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); 
});
    <script>
    function showValues() {
        var str = $("form").serialize();
        $.post('loginUser.html', 
        str,
        function(responseText, responseStatus, responseXML){
            if(responseStatus=="success"){
                window.location= "adminIndex.html";
            }
        });     
    }
</script>

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

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

Это имеет какой-то смысл для вас?

Хотя ответы, кажется, работают для людей, если вы используете Spring Security, я обнаружил, что расширение LoginUrlAuthenticationEntryPoint и добавление специального кода для обработки AJAX более надежны. Большинство примеров перехватывает все перенаправления, а не только ошибки аутентификации. Это было нежелательно для проекта, над которым я работаю. Может также возникнуть необходимость расширить ExceptionTranslationFilter и переопределить метод "sendStartAuthentication", чтобы удалить шаг кэширования, если вы не хотите, чтобы неудачный запрос AJAX кэшировался.

Пример 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

Пытаться

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

Поместите это на страницу входа. Если он был загружен в div на главной странице, он будет перенаправлять на страницу входа. "#site" - это идентификатор div, который находится на всех страницах, кроме страницы входа.

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

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

Некоторые могут найти следующее полезным:

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

Вот что я сделал:

На любой запрос Ajax мой сервер будет возвращать ответ Json 200 "NEUT TO AUTHENTICATE" (если клиент должен пройти аутентификацию).

Простой пример на 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);
        }
    };      
});

И это все.

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

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

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

У меня не было никакого успеха с решением заголовка - они никогда не были подобраны в моем методе 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;
}

Наконец, я решаю проблему, добавив кастом 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...

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

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

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

В сервлете вы должны положить response.setStatus(response.SC_MOVED_PERMANENTLY);чтобы отправить "301" xmlHttp статус, необходимый для перенаправления...

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

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

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

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

Я просто хотел фиксировать любые запросы 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");
}

У меня возникла эта проблема в приложении django, с которым я работаю (отказ от ответственности: я стараюсь учиться и ни в коем случае не являюсь экспертом). Я хотел использовать jQuery ajax для отправки запроса DELETE на ресурс, удалить его на стороне сервера, а затем отправить перенаправление обратно (в основном) на домашнюю страницу. Когда я отправил HttpResponseRedirect('/the-redirect/') Из скрипта Python метод jQuery ajax получал 200 вместо 302. Итак, я отправил ответ 300 с:

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

Затем я отправил / обработал запрос на клиенте с помощью jQuery.ajax так:

<button onclick="*the-jquery*">Delete</button>

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

Может быть, использование 300 не "правильно", но по крайней мере это сработало так, как я хотел.

PS: редактировать мобильную версию SO было огромной болью. Глупый интернет-провайдер выполнил мой запрос на отмену услуги, когда я закончил с моим ответом!

Вы также можете перехватить XMLHttpRequest отправить прототип. Это будет работать для всех посылок (jQuery/dojo/etc) с одним обработчиком.

Я написал этот код для обработки ошибки 500 страниц с истекшим сроком действия, но он должен работать так же хорошо, чтобы перехватить перенаправление 200. Подготовьте запись в Википедии на XMLHttpRequest onreadystatechange о значении 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);
}

Я получил рабочее решение, используя ответы из ссылок @John и @Arpad и ссылки @RobWinch

Я использую 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, как показано здесь

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

Я надеюсь, что это помогает другим.

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

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

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

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

UPD: Оппс. Есть та же задача, но она не работает. Заниматься этим Я покажу вам решение, когда найду его.

Другие вопросы по тегам