我想要实现的目标
如果$ 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);
答案 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) {
循环依赖性异常可能表明您的应用程序中存在混合的问题,这可能会导致稳定性问题。如果您发现自己遇到此异常,则应花时间查看您的体系结构,以确保避免任何最终引用自身的依赖项。
我同意下面的答案,使用$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');
});
});
这样你就分开了关注点:
答案 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");
}
});