Обработка ответа HTTP 302 от прокси-сервера в angularjs
У меня есть обратный прокси-сервер, который проверяет глобальную аутентификацию для нескольких приложений. Когда пользователь отключен, но все еще пытается использовать мое приложение, прокси отправляет ответ 302:
HTTP/1.1 302 Found
Date: Wed, 11 Sep 2013 09:05:34 GMT
Cache-Control: no-store
Location: https://other.url.com/globalLoginPage.html
Content-Length: 561
Content-Type: text/html; charset=iso-8859-1
Via: 1.1 my-proxy.com
Connection: Keep-Alive
В angularJs вызывается обратный вызов ошибки, но заголовки ответа пустые, статус 0, а данные - пустая строка. Поэтому кажется, что я действительно не могу ничего сделать, чтобы справиться с ответом...
Я видел несколько вопросов по этому вопросу, и я до сих пор не понимаю, что происходит (CORS из-за прокси или другого домена в местоположении?, Поведение браузера 302?).
В частности, есть эта часть ответа (qaru.site/info/210021/...):
Примечание. Если ваш сервер устанавливает код ответа 301 или 302, вы не сможете получить заголовок Location, так как он будет автоматически и прозрачно следовать за объектом XMLHttpRequest.
Как насчет этого объекта XMLHttpRequest?
В очень старой версии Chrome (не могу использовать более новую версию) я вижу, что соответствующий запрос на панели сети, но, похоже, не работает, поскольку ответа нет.
В последней версии firefox ничего не происходит.
Могу ли я что-нибудь сделать с этим, так как я не могу изменить конфигурацию прокси-сервера и ответ?
Update:
Сегодня я переиграл свой сценарий, и благодаря новой версии firebug я смог получить более подробную информацию о том, что происходит.
Я был недалеко от андерсея в моем вопросе: перекрестная политика домена.
Поскольку это HTTP-запрос, сделанный моим приложением, браузер отказывает в следующем XMLHttpRequest (которое в приложении похоже на тот же запрос). Отсюда ошибка и пустой ответ.
Поэтому я думаю, что я ничего особенного не могу с этим сделать
Ответы
Ответ 1
У меня была такая же проблема в моем приложении.
Вы не можете "поймать" ответ перенаправления 302. Браузер ловит его перед тем, как Angular получит его. так что на самом деле, когда вы получите ответ - уже слишком поздно.
Плохая новость: это не проблема на платформе Angular. XmlHttpRequest не поддерживает такое поведение, и браузер действует, прежде чем вы сможете что-либо сделать.
ссылка: Предотвратить перенаправление Xmlhttprequest
Хорошая новость: есть много способов обойти эту проблему, перехватив ответ и найти способ распознать, что это ваш любимый 302. Это взломать, но это лучший из вас может сделать в данный момент.
Так.
Например, в моем приложении перенаправление было возвращено на страницу login.html приложения, и в приложении я получил ответ с статусом 200, и данные ответа были содержимым моей страницы login.html.
поэтому в моем перехватчике я проверял, является ли результат строкой (обычно это не так, если нет проблемы с эффективностью), и если это так проверено, если это моя страница login.html. таким образом, я мог поймать перенаправление и обработать его по-своему.
yourApp.factory('redirectInterceptor', ['$location', '$q', function($location, $q) {
return function(promise) {
promise.then(
function(response) {
if (typeof response.data === 'string') {
if (response.data.indexOf instanceof Function &&
response.data.indexOf('<html id="ng-app" ng-app="loginApp">') != -1) {
$location.path("/logout");
window.location = url + "logout"; // just in case
}
}
return response;
},
function(response) {
return $q.reject(response);
}
);
return promise;
};
}]);
Затем вставьте этот перехватчик в свое приложение. что-то вроде этого:
$httpProvider.responseInterceptors.push('redirectInterceptor');
удачи.
Ответ 2
Ваш 302-Redirect обрабатывается непосредственно браузером, и вы ничего не можете с ним поделать напрямую. Однако вы можете использовать httpInterceptor
, чтобы помочь вам. Вам нужно будет включить $httpProvider
в список DI вашего приложения, а затем где-то в вашей функции конфигурации поместите ссылку на него следующим образом:
$httpProvider.responseInterceptors.push('HttpInterceptor');
Примерный перехватчик выглядит следующим образом:
window.angular.module('HttpInterceptor', [])
.factory('HttpInterceptor', ['$q', '$injector',
function($q, $injector) {
'use strict';
return function(promise) {
return promise.then(success, error);
};
function success(response) {
return response;
}
function error(response) {
var isAuthRequest = (response.config.url.indexOf('/v1/rest/auth') !== -1);
//if we are on the authenticating don't open the redirect dialog
if (isAuthRequest) {
return $q.reject(response);
}
//open dialog and return rejected promise
openErrorDialog(response);
return $q.reject(response);
}
function openErrorDialog(response) {
$injector.get('$dialog').dialog({
backdropFade: true,
dialogFade: true,
dialogClass: 'modal newCustomerModal',
resolve: {
errorData: function() {
return response.data;
},
errorStatus: function() {
return response.status;
}
}
})
.open('/views/error-dialog-partial.htm',
'errorDialogController')
.then(function(response) {
if (response) {
window.location = '/';
}
});
}
}
]);
Ответ 3
У меня была очень похожая проблема, и я рассмотрел решение, предоставленное Ofer Segev, но проверяя содержание ответа, чтобы увидеть, соответствует ли он html-фрагмент другой страницы, просто показалось мне слишком хриплым. Что происходит, когда кто-то меняет эту страницу?
К счастью, я также имел контроль над бэкэнд, поэтому вместо того, чтобы возвращать 302 (перенаправление), я вернул 403 (Запрещено) и передал нужное место в заголовках. В отличие от 302, 403 будет обрабатываться в вашем обработчике ошибок, где вы можете решить, что делать дальше. Вот мой обработчик:
function ($scope, $http) {
$http.get(_localAPIURL).then(function (response) {
// do what I'd normally do
}, function (response) {
if (response.status == 403)
{
window.location.href = response.headers().location;
}
else
{
// handle the error
}
Ответ 4
Как обсуждалось выше, 302 ответа не доступны для Angular, поскольку xmlHttpRequest не поддерживает возвратные перенаправления; браузер действует, прежде чем вы сможете что-либо сделать.
В дополнение к обработке пользовательских ответов данных и переопределению кодов состояния,
вы можете использовать более общее решение и добавить настраиваемый заголовок перенаправления, такой как X-Redirect
и действовать по этому поводу. Значение X-Redirect
должно быть URL-адресом, который вы хотите перенаправить, например, https://other.url.com/globalLoginPage.html
$http.get(orig_url).then(function(response) {
// do stuff
}, function (response) {
var headers = response.headers();
if ('x-redirect' in headers)
{
document.location.href = headers['x-redirect'];
}
return response;
}
}
Для более глобального решения вы также можете использовать HTTP-перехватчик
app.factory('myHttpInterceptor', function ($q, myCache) {
return {
request: function (config) {
return config;
},
response: function(response){
var headers = response.headers();
if ('x-redirect' in headers)
{
document.location.href = headers['x-redirect'];
}
return response;
},
responseError: function(rejection) {
switch(rejection.status)
{
case 302:
// this will not occur, use the custom header X-Redirect instead
break;
case 403:
alert.error(rejection.data, 'Forbidden');
break;
}
return $q.reject(rejection);
}
};
});
Вы можете настроить пользовательский сервер заголовка.
Например, если вы используете PHP Symfony:
$response = new Response('', 204);
$response->headers->set('X-Redirect', $this->generateUrl('other_url'));
return $response;