Я использую $.post() для вызова сервлета с помощью Ajax, а затем использую полученный фрагмент HTML для замены элемента div на текущей странице пользователя. Однако, если время сеанса истекает, сервер отправляет директиву перенаправления, чтобы отправить пользователя на страницу входа. В этом случае jQuery заменяет элемент div содержимым страницы входа в систему, заставляя глаза пользователя действительно наблюдать редкую сцену.
Как я могу управлять директивой перенаправления из вызова Ajax с помощью jQuery 1.2.6?
См. Связанный вопрос: stackoverflow.com/questions/5941933/…
HttpContext.Response.AddHeader и проверка успешности ajaxsetup - лучший способ
Почему сервер не может вернуть 401? В этом случае вы можете иметь глобальный $ .ajaxSetup и использовать код состояния для перенаправления страницы.
эта ссылка doanduyhai.wordpress.com/2012/04/21/… дает мне правильное решение



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Воспользуйтесь низкоуровневым вызовом $.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';
}
Я пытался избегать вещей низкого уровня. В любом случае, предположим, что я использую что-то вроде того, что вы описываете, как мне принудительно перенаправить браузер, когда я обнаруживаю, что код HTTP - 3xx? Моя цель - перенаправить пользователя, а не просто объявить, что его сеанс истек.
Кстати, $ .ajax () не очень-очень низкоуровневый. Это просто низкоуровневый с точки зрения jQuery, потому что есть $ .get, $ .post и т.д., которые намного проще, чем $ .ajax и все его параметры.
Да, сейчас я использую JQuery не на самом низком уровне, я был драматичен! Кстати, ваш ответ был действительно полезен. Спасибо!
О, парень! Извините, мне пришлось «отменить» ваш ответ, он все еще очень полезен. Дело в том, что перенаправления автоматически управляются XMLHttpRequest, поэтому я ВСЕГДА получаю код состояния 200 после перенаправления (вздох!). Думаю, мне придется сделать что-нибудь неприятное, например, разобрать HTML и поискать маркер.
Я поправил код. Спасибо. Извини, что ничем не мог больше помочь. Между прочим, это одна из причин, по которой люди реализуют формат ответа (например, XML или JSON), который содержит более подробный статус и т. д. Посмотрите на это, возможно. Или дайте мне знать, я могу опубликовать пример, если вам нужно.
Просто любопытно, если сеанс заканчивается на сервере, не означает ли это, что сервер отправляет другой SESSIONID? не могли бы мы просто это обнаружить?
ПРИМЕЧАНИЕ. Это не работает для переадресации. ajax перейдет на новую страницу и вернет ее код состояния.
Вы всегда получаете код ответа 200 в случае ответа 302 от сервера. Браузер прозрачно обрабатывает это 302 и делает второй запрос к новому местоположению.
Кроме того, вы, вероятно, захотите перенаправить пользователя на URL-адрес, указанный в заголовках. В итоге это будет выглядеть так:
$.ajax({
//.... other definition
complete:function(xmlHttp){
if (xmlHttp.status.toString()[0]=='3'){
top.location.href = xmlHttp.getResponseHeader('Location');
}
});
UPD: Оппс. Задача та же, но она не работает. Делаю это. Я покажу вам решение, когда найду его.
Этот ответ должен быть удален автором, так как он сам говорит, что это не работает. Он может опубликовать рабочее решение позже. -1
Идея хороша, но проблема в том, что заголовок «Местоположение» никогда не передается.
Мое редактирование было отклонено, поэтому ... вы должны использовать "var xmlHttp = $ .ajax ({...." переменная не может быть внутри ... затем используйте: console.info (xmlHttp.getAllResponseHeaders ()) ;
в сервлете вы должны поместить
response.setStatus(response.SC_MOVED_PERMANENTLY);
чтобы отправить статус xmlHttp '301', необходимый для перенаправления ...
а в функции $ .ajax вы не должны использовать функцию .toString() ..., просто
if (xmlHttp.status == 301) {
top.location.href = 'xxxx.jsp';
}
проблема в том, что он не очень гибкий, вы не можете решить, куда хотите перенаправить ..
перенаправление через сервлеты должно быть лучшим способом. но я все еще не могу найти правильный способ сделать это.
Ни один из браузеров не обрабатывает ответы 301 и 302 правильно. Фактически, стандарт даже говорит, что они должны обрабатывать их «прозрачно», что является ОГРОМНОЙ головной болью для поставщиков библиотек Ajax. В Ра-Аякс мы были вынуждены использовать код состояния HTTP-ответа 278 (просто некоторый «неиспользованный» код успеха) для прозрачной обработки перенаправлений с сервера ...
Это меня действительно раздражает, и если у кого-то здесь есть «тяга» к W3C, я был бы признателен, если бы вы позволили W3C знать, что нам действительно нужно обрабатывать коды 301 и 302 самостоятельно ...! ;)
Я полагаю, что 278 должен стать частью официальной спецификации HTTP.
Разве он уже не обрабатывает их прозрачно? Если ресурс переместился, прозрачная обработка его означает повторение запроса по предоставленному URL-адресу. Это то, чего я ожидал, используя API XMLHttpRequest.
@ PhilippeRathé Согласен. Прозрачное обращение - это как раз то, что я хочу. И я не знаю, почему это считается плохим.
@smwikipedia Чтобы организовать перенаправление разметки в основном разделе без перенаправления страницы.
Если у кого-то здесь есть "тяга" к W3C, я был бы признателен, если бы вы сообщили W3C, что нам действительно нужно обрабатывать коды 301 и 302 самостоятельно ...! ;)
Браузеры обрабатывают это прозрачно с первого дня. Что такое запрос AJAX? Это когда вы хотите, чтобы браузер выполнил запрос и вернул результат. ты хочет особого случая: «... вернуть результат, но в случае перенаправления не вернуть результат». Это может раздражать вас как разработчика библиотеки, потому что вы пытаетесь справиться с общим случаем, изменить способ работы браузеров. Но раз уж вы это сделаете, вам следует ожидать неприятностей.
... В общем случае это проблема, но когда это нужно? OP - единственный известный мне случай, который покрывается комментарий.
См. Также этот комментарий.
Решение, которое в конечном итоге было реализовано, заключалось в использовании оболочки для функции обратного вызова вызова 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, который мы используем для замены части страницы. Кроме того, нам нужно было только перенаправить на страницу входа.
Обратите внимание, что это можно сократить до function cbWrapper (funct) {return function (data) {if ($ ("# myForm", data) .size ()> 0) top.location.href = "login"; еще функция (данные); }}. Тогда вам понадобится только cbWrapper (myActualCB) при вызове .post. Да, код в комментариях - беспорядок, но это нужно отметить :)
размер обесценивается, поэтому вы можете использовать здесь .length вместо размера
Я решил эту проблему:
Добавление настраиваемого заголовка в ответ:
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 = '/';
}
});
Какое отличное решение. Мне нравится идея универсального решения. Мне нужно проверить статус 403, но для этого я могу использовать привязку ajaxSuccess к телу (это то, что я действительно искал). Спасибо.
Я только что сделал это и обнаружил, что мне нужен ajaxComplete там, где я использовал функцию $ .get (), и любой статус, кроме 200, не запускался. Фактически, я мог бы вместо этого просто привязаться к ajaxError. См. Мой ответ ниже для более подробной информации.
Это отлично сработало для меня. Просто установите javascript для перезагрузки текущей страницы с помощью location.reload();, поскольку наша модель требует перенаправления на отдельный сайт авторизации.
Во многих случаях это нормально, но что, если ваша структура обрабатывает авторизацию?
Это решение требует, чтобы браузер знал URL-адрес для аутентификации. Хотя это решает проблему, это (на мой взгляд) не обслуживаемое решение. Если контроллер аутентификации изменится, теперь вы обслуживаете файл маршрутов и javascript. т.е. это плохая практика программирования.
Мне нравится подход заголовка, но я также думаю, как @ mwoods79, что знание того, куда перенаправить, не должно дублироваться. Я решил это, добавив заголовок REDIRECT_LOCATION вместо логического.
Позаботьтесь о том, чтобы установить заголовок в ответе ПОСЛЕ перенаправления. Как описано в других ответах на этой странице, перенаправление может быть прозрачным для обработчика ajaxSucces. Таким образом, я включил заголовок в ответ GET страницы входа в систему (который в конечном итоге запускал ajaxSuccess только в моем сценарии).
И вы можете добавить эту проверку где-нибудь в Global.asax. В событии Application_AuthenticateRequest
Кто-нибудь знает, как это сделать в Spring Security?
Собирая воедино слова Владимира Прудникова и Томаса Хансена:
if request.is_ajax(): response.status_code = 278
Это заставляет браузер рассматривать ответ как успешный и передавать его вашему Javascript.
$('#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); });
Я прочитал этот вопрос и реализовал подход, который был заявлен относительно установки ответа Код состояния HTTP на 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. Я обнаружил, что этот подход намного лучше.
Как указано в решении в stackoverflow.com/questions/503093/…, лучше использовать window.location.replace (data.redirect); чем window.location.href = data.redirect;
Любая причина, по которой было бы лучше использовать HTTP-коды в действии. Например, код 307, который является временным перенаправлением HTTP?
@Sergei Golos, причина в том, что если вы выполняете перенаправление HTTP, перенаправление фактически никогда не достигает обратного вызова успеха ajax. Браузер обрабатывает перенаправление, доставляя код 200 с содержимым места назначения перенаправления.
как будет выглядеть код сервера? чтобы иметь возвращаемое значение data.form и data.redirect. в основном, как мне определить, что я помещаю в него перенаправление?
Этот ответ был бы более полезным, если бы он показал, как это делается на сервере.
200 кодов - не единственные коды, которые jQuery ajax считает успешными. Он также считает 304 (не измененный) успешным запросом и запускает обратный вызов успеха. Обычно я использую код состояния HTTP, чтобы указать успех как логическое значение, возвращая либо 200 в случае неудачи, либо 201 в случае успеха. Это отлично работает для MVC, потому что во время сбоя вы обычно хотите повторно отобразить страницу с ошибками модели, поэтому вы хотите вернуть 200. В случае успеха вы можете или не захотите возвращать и отображать данные, но в любом случае это очень легко проверить, имеет ли статус 201 (для успеха) или нет.
@Steg Почему НЕ позволяет браузеру обрабатывать 302 прозрачно? Разве этого не достаточно?
я хочу перенаправить пользователя на текущую страницу, я пытаюсь, это не работает для меня
Это не RESTful. Вы в основном делаете SOAP с JSON вместо XML.
@smwikipedia, потому что иногда ответ - это экран входа в систему
@ informatik01 Если мы добавим window.location.href для перенаправления, мы не сможем добавить пользовательские заголовки с запросом. Правильно? Как управлять отправкой пользовательских заголовков также при запросе перенаправления? Есть ли способ для этого?
@das Используя window.location.href, вы не можете добавлять собственные заголовки HTTP. Прочтите здесь и здесь для обсуждения связанных вопросов.
@ CarlesBarrobés Возможно, лучше использовать localtion.replace(), если вы отображаете страницу Только для перенаправления. Если вы отображаете страницу, через некоторое время делаете запрос ajax, а затем выполняете location.replace(), вы исключаете свою страницу из истории, что, скорее всего, не то, что вы хотите.
Рассматриваемый случай покрывается комментарий. В других случаях ... Почему вы хотите, чтобы сервер подсказывал вам, что делать (куда перенаправлять)? Это делает взаимодействие более сложным. Другими словами, передняя и задняя части становятся больше соединенный.
Я просто хотел зафиксировать любые запросы 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");
}
последний скроет любые другие ошибки, что затруднит устранение неполадок.
403 не означает, что пользователь не аутентифицирован, это означает, что (возможно, аутентифицированный) пользователь не имеет разрешения на просмотр запрошенного ресурса. Поэтому он не должен перенаправлять на страницу входа
У меня есть простое решение, которое мне подходит, не нужно менять код сервера ... просто добавьте чайную ложку мускатного ореха ...
$(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 для поиска любой уникальной строки, существующей на вашей странице входа ...
Это, похоже, не работает для меня, он продолжает вызывать функцию, определенную с помощью вызова ajax, как будто это не отменяет метод успеха.
Похоже, у меня это не работает, по крайней мере, больше. Возможное объяснение здесь: stackoverflow.com/a/12010724/260665
Пытаться
$(document).ready(function () {
if ($("#site").length > 0) {
window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
}
});
Поместите его на страницу входа в систему. Если он был загружен в div на главной странице, он будет перенаправлен на страницу входа. «#site» - это идентификатор div, который находится на всех страницах, кроме страницы входа.
Мне нравится метод Тиммерца с легким привкусом лимона. Если вам когда-либо вернется Тип содержимого из текст / 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();
}
});
Большое спасибо, Брайан, ваш ответ был лучшим для моего сценария, хотя я бы хотел, чтобы была более безопасная проверка, такая как сравнение URL-адреса / страницы, на которую выполняется перенаправление, вместо простой проверки типа содержимого. Мне не удалось найти, на какую страницу выполняется перенаправление с объекта jqXHR.
Я проверил статус 401 и затем перенаправил. Работает как чемпион.
У меня возникла эта проблема в приложении 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. Глупый интернет-провайдер отправил мой запрос на отмену услуги сразу после того, как я закончил со своим ответом!
<script>
function showValues() {
var str = $("form").serialize();
$.post('loginUser.html',
str,
function(responseText, responseStatus, responseXML){
if (responseStatus= = "success"){
window.location= "adminIndex.html";
}
});
}
</script>
Я решил это, разместив на моей странице 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;
}
}
}
Вы также можете подключить прототип отправки XMLHttpRequest. Это будет работать для всех отправлений (jQuery / dojo / и т. д.) С одним обработчиком.
Я написал этот код для обработки ошибки с истекшим сроком действия 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);
}
Я просто хотел поделиться своим подходом, так как это может кому-то помочь:
Я в основном включил модуль 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 ...
+1 - но пользовательские заголовки должны начинаться с X-, поэтому лучше использовать заголовок X-LoginPage: http://example.com/login.
@ShaquinTrifonoff Больше нет. Я не использовал префикс X-, потому что в Июнь 2011 г. в документе ITEF предлагалось отказаться от них, и действительно, с Июнь 2012 г. не является официальным, что пользовательские заголовки больше не должны иметь префикс X-.
У нас также есть ISA-сервер, и я столкнулся с той же проблемой. Вместо того, чтобы обойти это в коде, мы использовали инструкции в kb2596444, чтобы настроить ISA для прекращения перенаправления.
Я думаю, что лучший способ справиться с этим - использовать существующие коды ответов протокола HTTP, в частности 401 Unauthorized.
Вот как я это решил:
Клиентская сторона: привязка к событиям 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';
}
});
ИМО, это более общий характер, и вы не пишете какую-то новую специальную спецификацию / заголовок. Вам также не придется изменять какие-либо из ваших существующих вызовов ajax.
Редактировать: Согласно приведенному ниже комментарию @ Rob, индикатором должен быть 401 (код состояния HTTP для ошибок аутентификации). Подробнее см. 403 Forbidden и 401 Unauthorized HTTP-ответы. При этом некоторые веб-фреймворки используют 403 как для проверки подлинности, так и для ошибок авторизации - поэтому адаптируйтесь соответствующим образом. Спасибо, Роб.
Я использую тот же подход. Действительно ли jQuery вызывает ajaxSuccess с кодом ошибки 403? Я думаю, что на самом деле нужна только часть ajaxError
работает у меня .. Спасибо
Если вы также хотите передать значения, вы также можете установить переменные сеанса и получить доступ Например: В своем 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;
}
Я знаю, что эта тема старая, но я предложу еще один подход, который я нашел и описал ранее: здесь. В основном я использую 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;
}
}
});
}
Я исправил проблему, используя событие PostAuthenticateRequest вместо события EndRequest.
@JaroslawWaliszko Я вставил не то событие в свой последний ответ! Я имел в виду событие PreSendRequestHeaders ..... не PostAuthenticateRequest! >> blush << спасибо, что указали на мою ошибку.
@JaroslawWaliszko при использовании WIF вы также можете вернуть 401 ответ на запросы AJAX и позволить вашему javascript обрабатывать их. Также вы предполагаете, что все 302 требуют аутентификации, которая может быть верной не во всех случаях. Добавил ответ, если кому интересно.
Хотя ответы, похоже, работают для людей, если вы используете 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"));
}
}
Было бы полезно (для меня), если бы проголосовавшие вниз объяснили, почему они голосуют против. Если с этим решением что-то не так, я хотел бы поучиться на своих ошибках. Спасибо.
Если вы используете Spring, а также JSF, также проверьте следующее: ("partial / ajax"). EqualsIgnoreCase (request.getHeader ("faces-r equest"));
Пользователи могли проголосовать против, потому что вы не упомянули: (1) необходимые клиентские моды для обнаружения вашего ответа об ошибке; (2) необходимые модификации для конфигурации Spring, чтобы добавить ваш настроенный LoginUrlAuthenticationEntryPoint в цепочку фильтров.
Ваш ответ похож на этот ответ от @Arpad. У меня это сработало; с использованием Spring Security 3.2.9. stackoverflow.com/a/8426947/4505142
В большинстве данных решений используется обходной путь с использованием дополнительного заголовка или несоответствующего 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);
}
});
Хорошее решение. Спасибо.
это сработало для меня:
success: function(data, textStatus, xhr) {
console.info(xhr.status);
}
в случае успеха ajax получит тот же код состояния, который браузер получает от сервера, и выполнит его.
Хм? это просто выводит на консоль статус запроса? Что для успеха будет 2хх.
Еще одно решение, которое я нашел (особенно полезное, если вы хотите установить глобальное поведение), - использовать $.ajaxsetup() метод вместе с statusCode недвижимость. Как указывалось другими, не используйте код состояния перенаправления (3xx), вместо этого используйте код состояния 4xx и обрабатывайте перенаправление на стороне клиента.
$.ajaxSetup({
statusCode : {
400 : function () {
window.location = "/";
}
}
});
Замените 400 на код состояния, который вы хотите обработать. Как уже упоминалось, 401 Unauthorized может быть хорошей идеей. Я использую 400, поскольку он очень неопределенный, и я могу использовать 401 в более конкретных случаях (например, неправильные учетные данные). Таким образом, вместо прямого перенаправления ваш бэкэнд должен возвращать код ошибки 4xx, когда время сеанса истекло, и вы обрабатываете перенаправление на стороне клиента. Идеально работает для меня даже с такими фреймворками, как backbone.js
Где упомянуть функцию на странице?
@Vikrant Если я правильно понимаю ваш вопрос, вы можете вызвать функцию сразу после загрузки jQuery и до того, как вы начнете выполнять свои фактические запросы.
Наконец, я решил проблему, добавив кастомный 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, ....
и другой важный момент - нет необходимости изменять код ни на стороне сервера, ни на стороне клиента.
и следующее - это возможность получить доступ к другой информации о действиях публикации, таких как ошибки, сообщения и ... Таким образом.
Я выложил это, может быть, кому-нибудь поможет :)
Некоторым может оказаться полезным следующее:
Я хотел, чтобы клиенты перенаправлялись на страницу входа в систему для любого rest-action, отправляемого без токена авторизации. Поскольку все мои остальные действия основаны на 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);
}
};
});
Вот и все.
Эта проблема может возникнуть при использовании метода 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'" выше, фильтр поймает это и оценит перенаправление вместо того, чтобы отображать данные.
data находится в теле ответа или заголовке?
Я получил рабочее решение, используя ответы @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 на странице браузера. Конечно, не то, что я хочу.
Обновление2
Вместо использования always-use-default-target = "true" используйте пример @RobWinch блокировки запросов AJAX из requstCache. Это позволяет перенаправлять обычные ссылки на их исходную цель после входа в систему, но AJAX переходит на домашнюю страницу после входа в систему.
Позвольте мне еще раз процитировать проблему, описанную @Steg
I had a similar problem to yours. I perform an ajax request that has 2 possible responses: one that redirects the browser to a new page and one that replaces an existing HTML form on the current page with a new one.
ИМХО, это реальная проблема, и ее нужно будет официально расширить до текущих стандартов 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.
Есть ли в этом смысл для вас?
Используйте параметр statusCode, как показано ниже, перенаправления обычно представляют собой коды состояния 301, 302 для перенаправлений.
$.ajax({
type: <HTTP_METHOD>,
url: {server.url},
data: {someData: true},
statusCode: {
301: function(responseObject, textStatus, errorThrown) {
//yor code goes here
},
302: function(responseObject, textStatus, errorThrown) {
//yor code goes here
}
}
})
.done(function(data){
alert(data);
})
.fail(function(jqXHR, textStatus){
alert('Something went wrong: ' + textStatus);
})
.always(function(jqXHR, textStatus) {
alert('Ajax request was finished')
});
какая версия jquery поддерживает параметр statusCode?
его там с версии 1.5, я думаю, api.jquery.com/jquery.ajax
Хорошо, в любом случае я искал перенаправление на стороне сервера, для которого код состояния будет 200
на каком сервере код редиректа 200?
на сервере фляги python
@GuruVishnuVardhanReddy, вы должны сначала просмотреть документацию, прежде чем публично раскрыть свое невежество flaskapi.org/api-guide/status-codes
Я не сказал, что ваше решение было неправильным, я сказал, что оно не сработало для меня, stackoverflow.com/questions/53651467/…
(не ответ как таковой) - я делал это в прошлом, редактируя библиотеку jquery и добавляя проверку страницы входа в каждый завершенный XHR. Не лучшее решение, потому что это придется делать каждый раз при обновлении, но оно решает проблему.