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

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

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

Ответы [ 31 ]

7 голосов
/ 09 апреля 2016

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

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

И это все.

5 голосов
/ 06 февраля 2013

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

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

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

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

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

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

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

5 голосов
/ 30 июля 2015

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

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

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

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

5 голосов
/ 07 января 2013

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

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

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

4 голосов
/ 28 августа 2011

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

4 голосов
/ 10 апреля 2012

Вы также можете перехватить отправку прототипа 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);
}
1 голос
/ 22 марта 2018

Я получил рабочее решение, используя ответы @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") в конфигурацию form-login. Это было необходимо, поскольку после того, как запрос AJAX перенаправляется на страницу входа в систему (из-за истекшего сеанса), Spring запоминает предыдущий запрос AJAX и автоматически перенаправляет его после входа в систему. Это приводит к отображению возвращенного JSON на странице браузера. Конечно, не то, что я хочу.

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

1 голос
/ 19 октября 2008

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

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

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

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