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

我正在努力实现的

如果$ http请求返回401错误,我想转换到某个状态(login)。 因此我创build了一个$ 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); 

修正

使用$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服务作为dependency injection到$TemplateFactory ,然后在调度拦截器时在$httpProvider自身内创build对$http的循环引用。

如果试图直接将$http服务注入到拦截器中,则会抛出相同的循环依赖exception。

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

关注点分离

循环依赖exception可能表明您的应用程序中存在可能导致稳定性问题的混合问题。 如果你发现自己有这个exception,你应该花时间看看你的架构,以确保你避免任何依赖,最终引用自己。

@Stephen Friedrich的回答

我同意下面的答案,使用$injector直接得到所需服务的参考是不理想的,可以被认为是反模式。

发射事件是一个更加优雅和解耦的解决scheme。

问题是AngularJS的重复:将服务注入HTTP拦截器(循环依赖)

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

一个更好的修复

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

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

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

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

这样你就分开了关注点:

  1. 检测401响应
  2. redirect到login

乔纳森的解决scheme是伟大的,直到我试图保存目前的状态。 在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"); } });