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

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

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

Ответ 1

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

Ответ 2

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

Ответ 3

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

  • Добавление настраиваемого заголовка в ответ:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  • Связывание функции JavaScript с событием ajaxSuccess и проверка наличия существующего заголовка:

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

Ответ 4

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

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

Ответ 5

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

});

Ответ 6

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

Ответ 7

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

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

Мой сценарий: у нас в основном есть ISA-сервер, между которым прослушиваются все запросы, а отвечает 302 и заголовок местоположения на нашу страницу входа.

В моем модуле JavaScript мой начальный подход был чем-то вроде

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

Проблема (как уже упоминалось выше) заключается в том, что браузер обрабатывает перенаправление самостоятельно, поэтому мой callback 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...

Ответ 8

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

Подход, показанный ниже, может применяться ко всем запросам ajax из коробки (если они явно не переопределяют событие sendSend).

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

Ответ 9

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

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

Ответ 10

Я думаю, что лучший способ справиться с этим - использовать существующие коды ответа HTTP-протокола, в частности 401 Unauthorized.

Вот как я это решил:

  • Серверная сторона: если сеанс истекает, а запрос - ajax. отправьте заголовок кода ответа 401
  • Клиентская сторона: привязка к событиям 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.

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

Ответ 11

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

Ответ 12

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

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

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

Ответ 13

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

Ответ 14

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

  • Измените свой серверный код, чтобы определить, является ли он XHR. Если это так, установите код ответа перенаправления на номер 278. В джанго:
   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); 
});

Ответ 15

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

Ответ 16

Try

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

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

Ответ 17

    <script>
    function showValues() {
        var str = $("form").serialize();
        $.post('loginUser.html', 
        str,
        function(responseText, responseStatus, responseXML){
            if(responseStatus=="success"){
                window.location= "adminIndex.html";
            }
        });     
    }
</script>

Ответ 18

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

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

Ответ 19

Хотя ответы, похоже, работают на людей, если вы используете 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

Ответ 20

Позвольте мне просто повторить проблему, описанную @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.

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

Ответ 21

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

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

И что об этом.

Ответ 22

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

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

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

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

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

Ответ 23

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

Ответ 24

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

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

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

Ответ 25

У меня не было никакого успеха в решении заголовка - они никогда не поднимались в моем методе ajaxSuccess/ajaxComplete. Я использовал ответ Steg с пользовательским ответом, но я немного изменил сторону JS. Я настраиваю метод, который я вызываю в каждой функции, поэтому я могу использовать стандартные методы $.get и $.post.

function handleAjaxResponse(data, callback) {
    //Try to convert and parse object
    try {
        if (jQuery.type(data) === "string") {
            data = jQuery.parseJSON(data);
        }
        if (data.error) {
            if (data.error == 'login') {
                window.location.reload();
                return;
            }
            else if (data.error.length > 0) {
                alert(data.error);
                return;
            }
        }
    }
    catch(ex) { }

    if (callback) {
        callback(data);
    }
}

Пример использования...

function submitAjaxForm(form, url, action) {
    //Lock form
    form.find('.ajax-submit').hide();
    form.find('.loader').show();

    $.post(url, form.serialize(), function (d) {
        //Unlock form
        form.find('.ajax-submit').show();
        form.find('.loader').hide();

        handleAjaxResponse(d, function (data) {
            // ... more code for if auth passes ...
        });
    });
    return false;
}

Ответ 26

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

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

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

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

Ответ 27

У меня была эта проблема в приложении django, с которым я возился (отказ от ответственности: я возился, чтобы учиться, и я никоим образом не специалист). То, что я хотел сделать, это использовать jQuery ajax для отправки запроса DELETE на ресурс, удалить его на стороне сервера, а затем отправить перенаправление обратно (в основном) на главную страницу. Когда я отправил HttpResponseRedirect('/the-redirect/') из python script, метод 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. Глупый интернет-провайдер подал запрос на отмену моего обслуживания, когда я закончил с моим ответом!

Ответ 28

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

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

Ответ 29

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

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

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

Ответ 30

У меня есть рабочий solulion, используя ответы от @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");
//            }
        });

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

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

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