Handle HTTP 302 response from proxy in angularjs
我有一个反向代理,用于检查几个应用程序的全局身份验证。当用户断开连接但仍尝试使用我的应用程序时,代理将发送302响应:
1 2 3 4 5 6 7 8 | 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://stackoverflow.com/a/17903447/1093925):
Note: If your server sets a response code of 301 or 302, you will not be able to get the Location header, as it will be automatically and transparently followed by the XMLHttpRequest object.
这个XMLHttpRequest对象呢?
在非常旧的Chrome版本(不能使用较新的版本)中,我可以在网络面板中看到相应的请求,但是由于没有响应,它似乎失败了。
在最新版本的Firefox中,没有任何反应。
由于无法更改代理配置和响应,我能对此采取任何措施吗?
更新:
我今天重播了我的场景,并且由于有了较新版本的Firebug,我能够获得有关正在发生的事情的更多详细信息。
我的问题离解决方案不远:跨域策略。
由于这是我的应用程序发出的HTTP请求,因此浏览器会拒绝以下XMLHttpRequest(应用程序内看起来像同一请求)。因此,错误和空响应。
所以我认为我对此无能为力
我的应用程式中有同样的问题。
您无法真正"捕获" 302重定向响应。浏览器会在Angular获得它之前将其捕获。所以实际上,当您收到答复时-已经为时已晚。
坏消息:在角度平台中这不是问题。 xmlHttpRequest不支持这种行为,浏览器会在对此采取任何措施之前采取行动。
参考:防止Xmlhttprequest重定向
好消息:有很多方法可以通过拦截响应来绕过此问题,并找到某种方式来识别它是您的可爱302。这是一个hack,但这是您目前能做的最好的事情。
所以。
例如,在我的应用程序中,重定向返回到该应用程序的login.html页面,在该应用程序中,我收到的响应状态为200,响应的数据就是我的login.html页面的内容。
因此,在我的拦截器中,我检查了结果是否为字符串(通常不是!所以-没有效率问题。),如果是,请检查其是否为我的login.html页面。这样,我可以捕获重定向并以自己的方式进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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; }; }]); |
然后将此拦截器插入您的应用程序。像这样的东西:
1 | $httpProvider.responseInterceptors.push('redirectInterceptor'); |
祝好运。
您的302-重定向是由浏览器直接处理的,您无法直接对其进行任何处理。但是,您可以使用
1 | $httpProvider.responseInterceptors.push('HttpInterceptor'); |
一个示例拦截器如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 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 = '/'; } }); } } ]); |
如上所述,由于xmlHttpRequest不支持返回重定向,因此302对Angular不可用。浏览器在执行任何操作之前会起作用。
除了处理自定义数据响应和覆盖状态代码外,
您可以使用更通用的解决方案并添加自定义重定向标头(例如
1 2 3 4 5 6 7 8 9 10 11 | $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拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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:
1 2 3 | $response = new Response('', 204); $response->headers->set('X-Redirect', $this->generateUrl('other_url')); return $response; |
我有一个非常相似的问题,并考虑了Ofer Segev提供的解决方案,但是检查响应的内容以查看它是否与另一个页面的html片段匹配对我来说似乎太过分了。有人更改该页面会怎样?
幸运的是,我也控制了后端,所以我没有返回302(重定向),而是返回了403(禁止访问),并在标头中传递了所需的位置。与302不同,403将在您的错误处理程序中处理,您可以在其中决定下一步要做什么。这是我得到的处理程序:
1 2 3 4 5 6 7 8 9 10 11 12 | 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 } |
仅供参考,以防万一仍然有人在看这个:您还可以进行调用,以使Iframe返回302。这样,您就可以停留在页面上,并且可以与https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage通信