将$ 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'); }); });
这样你就分开了关注点:
- 检测401响应
- 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"); } });