web-dev-qa-db-ja.com

angularjsのプロキシからのHTTP 302応答を処理する

複数のアプリケーションのグローバル認証をチェックするリバースプロキシがあります。ユーザーが切断されているにもかかわらず、アプリケーションを使用しようとすると、プロキシは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の応答コードを設定すると、XMLHttpRequestオブジェクトが自動的かつ透過的に追跡されるため、Locationヘッダーを取得できません。

このXMLHttpRequestオブジェクトはどうですか?
非常に古いバージョンのChrome(新しいバージョンを使用できません)ネットワークパネルで対応するリクエストを確認できますが、応答なし。
Firefoxの最新バージョンでは、何も実行されていません。

プロキシの構成と応答を変更できないので、それについて何かできますか?

更新:
今日、シナリオを再生しました。firebugの新しいバージョンのおかげで、何が起こっているかについての詳細を取得することができました。
クロスドメインポリシーという質問で、私はanwserからそれほど遠くありませんでした。
これはアプリケーションによって作成されたHTTPリクエストであるため、ブラウザは次のXMLHttpRequest(アプリ内では同じリクエストのように見えます)を拒否します。したがって、エラーと空の応答。
だから、特別なことは何もできないと思う

34
D0m3

アプリでも同じ問題が発生しました。本当に302リダイレクト応答を「キャッチ」することはできません。ブラウザはAngularを手に入れる前にそれをキャッチします。実際に、あなたがあなたの応答を受け取ったとき-それはすでに手遅れです。

悪いニュース:angularプラットフォームでは問題ではありません。xmlHttpRequestはこのような動作をサポートしていません。そして、ブラウザはそれについて何かをする前に動作します。参照: Xmlhttprequestのリダイレクトを防ぐ

朗報:応答を傍受することで、この問題を回避する方法がたくさんあります。ハックですが、現時点でできる最善の方法です。

So。たとえば、私のアプリでは、リダイレクトはアプリの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');

がんばろう。

43
Ofer Segev

302 -Redirectはブラウザによって直接処理されており、直接行うことはできません。ただし、あなたはhttpInterceptorを使用して手助けすることができます。アプリのDIリストに$httpProviderを含める必要があります。次に、config関数のどこかに次のように参照を配置します。

$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
MBielski

私は非常に似た問題を抱えており、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
        }
1
plunkg

上記で説明したように、302応答はAngularでは利用できません。xmlHttpRequestはリダイレクトを返すことをサポートしていないため、ブラウザは何もする前に動作します。

カスタムデータレスポンスの処理とステータスコードのオーバーライドに加えて、
より汎用的なソリューションを使用して、X-Redirectなどのカスタムリダイレクトヘッダーを追加し、それに基づいて行動することができます。 X-Redirectの値は、たとえばhttps://other.url.com/globalLoginPage.htmlにリダイレクトするURLである必要があります

$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;
0
browniebytes