在angularjs中处理来自代理的HTTP 302响应
Posted
技术标签:
【中文标题】在angularjs中处理来自代理的HTTP 302响应【英文标题】:Handle HTTP 302 response from proxy in angularjs 【发布时间】:2013-09-15 06:29:05 【问题描述】:我有一个反向代理来检查多个应用程序的全局身份验证。当用户断开连接但仍在尝试使用我的应用程序时,代理会发送 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 浏览器行为?)。 特别是答案中的这一部分(https://***.com/a/17903447/1093925):
注意:如果您的服务器设置的响应代码为 301 或 302,您将无法获取 Location 标头,因为 XMLHttpRequest 对象将自动透明地跟随它。
这个 XMLHttpRequest 对象呢? 在非常旧的 Chrome 版本(不能使用较新版本)中,我可以在网络面板中看到相应的请求,但由于没有响应,它似乎失败了。 在最新版本的 Firefox 中,没有发生任何事情。
由于无法更改代理配置和响应,我可以对此做些什么吗?
更新: 我今天重播了我的场景,并且感谢更新版本的萤火虫,我能够获得有关正在发生的事情的更多详细信息。 我离问题的答案不远:跨域策略。 由于它是我的应用程序发出的 HTTP 请求,因此浏览器拒绝以下 XMLHttpRequest(应用内看起来像相同的请求)。因此出现错误和空响应。 所以我觉得没什么特别的了
【问题讨论】:
有谁知道如何从 302 获取 AngularJS 中的Location
标头?它在浏览器中公开,但是在检查错误响应(配置、标头、数据等)时,它似乎不存在于 Angular 应用程序中……我需要将我的用户重定向到位置标题
【参考方案1】:
我在我的应用程序中遇到了同样的问题。 您无法真正“捕获” 302 重定向响应。浏览器在 Angular 处理它之前捕获它。所以实际上,当您收到回复时 - 已经太晚了。
坏消息:在 Angular 平台中这不是问题。 xmlHttpRequest 不支持这种行为,并且浏览器会在您对其进行任何操作之前采取行动。 参考:Prevent redirection of Xmlhttprequest
好消息:有很多方法可以绕过这个问题,通过拦截响应并找到一些方法来识别它是你可爱的 302。这是一个 hack,但它是最好的你目前可以。
所以。 例如,在我的应用程序中,重定向返回到应用程序的 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');
祝你好运。
【讨论】:
我很喜欢这种方式,我用它来抓取登录页面的标题,response.data.indexOf('您的 302 -Redirect 正在由浏览器直接处理,您无法直接对其执行任何操作。但是,您可以使用httpInterceptor
来帮助您。您需要在您的应用程序 DI 列表中包含 $httpProvider
,然后在您的配置函数中的某个位置对它进行引用,如下所示:
$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 = '/';
);
]);
【讨论】:
感谢您的回答,但我看不出它对我的问题有何帮助。在拦截器中,您仍然没有关于响应的信息。 嗯……你说得有道理。您是否尝试过通过 $route 处理路由更改? (docs.angularjs.org/api/ngRoute.$route) 其实我是做REST请求的,所以我相信和$route没有关系。 是的,但是你的路线不是因为302而改变吗?您可以在 $routeChangeStart 或 $routeChangeSuccess 中检测到该更改并对其做出反应。 不,用户没有被重定向。我在 angularjs 中发出一个 HTTP 请求(使用 $http),我得到一个错误的响应。没有其他事情发生。【参考方案3】:如上所述,Angular 无法使用 302 响应,因为 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;
【讨论】:
【参考方案4】:我有一个非常相似的问题,并考虑了 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
【讨论】:
如果您可以访问后端,这是一个更简洁的解决方案 我也有同样的想法,但如果是 web api 调用,我只想返回 401。这个SO question/answer 解决了这个问题。【参考方案5】:仅供参考,以防有人仍然看到此内容:您还可以进行预期返回 302 形式的 Iframe 调用。这样你就可以留在页面上,并且可以与https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage进行交流
【讨论】:
以上是关于在angularjs中处理来自代理的HTTP 302响应的主要内容,如果未能解决你的问题,请参考以下文章