43

我有一个反向代理来检查多个应用程序的全局身份验证。当用户断开连接但仍在尝试使用我的应用程序时,代理会发送 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://stackoverflow.com/a/17903447/1093925):

注意:如果您的服务器将响应代码设置为 301 或 302,您将无法获取 Location 标头,因为 XMLHttpRequest 对象将自动且透明地跟随它。

这个 XMLHttpRequest 对象呢?
在非常旧的 Chrome 版本(不能使用较新版本)中,我可以在网络面板中看到相应的请求,但由于没有响应,它似乎失败了。
在最新版本的 Firefox 中,没有发生任何事情。

由于无法更改代理配置和响应,我可以对此做些什么吗?

更新:
我今天重播了我的场景,并且感谢更新版本的萤火虫,我能够获得有关正在发生的事情的更多详细信息。
我离问题的答案不远:跨域策略。
由于它是我的应用程序发出的 HTTP 请求,因此浏览器拒绝以下 XMLHttpRequest(在应用程序中看起来像相同的请求)。因此出现错误和空响应。
所以我认为对此我无能为力

4

5 回答 5

47

我在我的应用程序中遇到了同样的问题。您无法真正“捕获” 302 重定向响应。浏览器在 Angular 处理它之前捕获它。所以实际上,当你收到你的回复时——已经太晚了。

坏消息:在角度平台中这不是问题。xmlHttpRequest 不支持这种行为,并且浏览器会在您对其进行任何操作之前采取行动。参考:防止 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');

祝你好运。

于 2014-07-16T16:17:20.493 回答
3

您的 302 -Redirect 正在由浏览器直接处理,您无法直接对其执行任何操作。但是,您可以使用 anhttpInterceptor来帮助您。您需要包含$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 = '/';
                }
            });
        }
    }
]);
于 2013-12-31T23:00:43.683 回答
3

如上所述,Angular 无法使用 302 响应,因为 xmlHttpRequest 不支持返回重定向;浏览器会在您对其进行任何操作之前采取行动。

除了处理自定义数据响应和覆盖状态代码之外,
您还可以使用更通用的解决方案并添加自定义重定向标头,例如X-Redirect并对其采取行动。的值X-Redirect应该是您要重定向到的网址,例如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;
于 2018-12-05T14:58:59.040 回答
2

我有一个非常相似的问题,并考虑了 Ofer Segev 提供的解决方案,但是检查响应的内容以查看它是否匹配另一个页面的 html 片段对我来说似乎太 hacky。当有人更改该页面时会发生什么?

幸运的是,我也控制了后端,所以我没有返回 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
        }
于 2016-09-20T17:28:56.350 回答
1

仅供参考,以防有人仍然看到这个:您还可以进行预期返回 302 形式的 Iframe 调用。这样您就可以留在页面上,并且可以与https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage进行通信

于 2020-05-20T09:12:46.877 回答