将$ state(ui-router)注入$ http拦截器会导致循环依赖

时间:2013-11-27 00:21:37

标签: angularjs angular-ui-router

我想要实现的目标

如果$ 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);

3 个答案:

答案 0 :(得分:212)

修复

使用$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 :(得分:25)

问题是AngularJS: Injecting service into a HTTP interceptor (Circular dependency)

的重复

我在这里重新发布我的答案:

更好的修复

我认为直接使用$ injector是一个反模式。

打破循环依赖的一种方法是使用一个事件: 而不是注入$ state,注入$ rootScope。 不要直接重定向,而是

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

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

这样你就分开了关注点:

  1. 检测401回复
  2. 重定向登录

答案 2 :(得分:16)

乔纳森的解决方案很棒,直到我试图保存当前状态。在ui-router 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");
        }
    });