$ state(ui-router)を$ httpインターセプターに注入すると循環依存が発生する


120

私が達成しようとしていること

$ httpリクエストが401エラーを返す場合、特定の状態(ログイン)に移行したいと思います。したがって、$ httpインターセプターを作成しました。

問題

'$ state'をインターセプターに挿入しようとすると、循環依存関係が発生します。なぜ、どうすれば修正できますか?

コード

//Inside Config function

    var interceptor = ['$location', '$q', '$state', function($location, $q, $state) {
        function success(response) {
            return response;
        }

        function error(response) {

            if(response.status === 401) {
                $state.transitionTo('public.login');
                return $q.reject(response);
            }
            else {
                return $q.reject(response);
            }
        }

        return function(promise) {
            return promise.then(success, error);
        }
    }];

    $httpProvider.responseInterceptors.push(interceptor);

回答:


213

修正

$injectorサービスを使用して、サービスへの参照を取得し$stateます。

var interceptor = ['$location', '$q', '$injector', function($location, $q, $injector) {
    function success(response) {
        return response;
    }

    function error(response) {

        if(response.status === 401) {
            $injector.get('$state').transitionTo('public.login');
            return $q.reject(response);
        }
        else {
            return $q.reject(response);
        }
    }

    return function(promise) {
        return promise.then(success, error);
    }
}];

$httpProvider.responseInterceptors.push(interceptor);

原因

angular-ui-routerは、$httpサービスを依存関係として挿入し、インターセプターをディスパッチすると、それ自体に$TemplateFactory循環参照を作成し$httpます$httpProvider

同様に$httpインターセプターにサービスを直接挿入しようとすると、同じ循環依存例外がスローされます。

var interceptor = ['$location', '$q', '$http', function($location, $q, $http) {

関心事の分離

循環依存の例外は、アプリケーション内に安定性の問題を引き起こす可能性のある懸念が混在していることを示している可能性があります。この例外に気づいた場合は、時間をかけてアーキテクチャーを調べ、最終的に自分自身を参照する依存関係を回避する必要があります。

@Stephen Friedrichの回答

を使用$injectorして目的のサービスへの参照を直接取得することは理想的ではなく、アンチパターンと見なすことができるという以下の回答に同意します。

イベントを発行することは、はるかにエレガントで、分離されたソリューションです。


1
これは、インターセプターに渡される4つの異なる関数があるAngular 1.3でどのように調整されますか?
Maciej Gurban 2014

3
使い方は同じで、$injectorインターセプターにサービスを注入し、サービス$injector.get()を取得する必要がある場所に呼び出します$state
Jonathan Palumbo、2014

$ injectot.get( "$ state")を<<未定義>>として取得しました。問題の原因を教えていただけませんか?
sandeep kale 2015

これは単純な状況では間違いなく命の恩人ですが、これに遭遇すると、実際にコードのどこかに循環依存関係が作成されたことを示しています。これは、AngularJSのDIで簡単に実行できます。Misko Heveryは彼のブログで非常によく説明しています。コードを改善するためにそれを読むことを強くお勧めします。
JD Smith

2
$httpProvider.responseInterceptors.pushAngularの新しいバージョンを使用している場合は動作しなくなります。$httpProvider.interceptors.push()代わりに使用してください。interceptorも変更する必要があります。とにかく、素晴らしい答えをありがとう!:)
shyam

25

質問はの複製です AngularJSの:HTTPインターセプターへのサービスの挿入(循環依存)

私はそのスレッドから私の答えをここに再投稿しています:

より良い修正

$ injectorを直接使用することはアンチパターンだと思います。

循環依存関係を解除する方法は、イベントを使用することです。$ stateを注入する代わりに、$ rootScopeを注入します。直接リダイレクトする代わりに、

this.$rootScope.$emit("unauthorized");

プラス

angular
    .module('foo')
    .run(function($rootScope, $state) {
        $rootScope.$on('unauthorized', () => {
            $state.transitionTo('login');
        });
    });

このようにして、懸念を分離しました。

  1. 401応答を検出する
  2. ログインにリダイレクト

2
実際、この質問はその前に尋ねられたので、この質問はこの質問の複製です。ただし、エレガントな修正が大好きなので、賛成投票をしてください。;-)
アーロングレイ

1
これをどこに置くか混乱しています。$ rootScope。$ emit( "unauthorized");
アレックス

16

現在の状態を保存しようとするまで、ジョナサンの解決策は素晴らしかった。ではUI-ルータv0.2.10現在の状態は迎撃の最初のページのロードに移入されていないようです。

とにかく、代わりに$ stateChangeErrorイベントを使用して解決しました。$ stateChangeErrorのイベントは、あなたの両方を与えるしてからの状態だけでなく、エラーが発生しました。それはかなり気の利いたです。

$rootScope.$on('$stateChangeError',
    function(event, toState, toParams, fromState, fromParams, error){
        console.log('stateChangeError');
        console.log(toState, toParams, fromState, fromParams, error);

        if(error.status == 401){
            console.log("401 detected. Redirecting...");

            authService.deniedState = toState.name;
            $state.go("login");
        }
    });

これに関する問題を提出しました。
Justin Wrobel、2014年

常に非同期なので、これはngResourceでは不可能だと思いますか?いくつか試してみましたが、状態の変更を防ぐためのリソース呼び出しを取得できません...
Bastian

4
状態変化を引き起こさないリクエストをインターセプトしたい場合はどうなりますか?
Shikloshi、2015年

@Stephen Friedrichが上で行ったのと同じアプローチを使用できます。これは実際にはこれに似ていますが、カスタムイベントを使用します。
saiyancoder
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.