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

我想达到的目标

如果 $http 请求返回401错误,我想转换到某种状态(登录)。因此,我创建了一个 $http 拦截器。

问题是

When I am trying to insert '$state' into the interceptor I get a circular dependency. Why and how do i fix it?

密码

//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);
43203 次浏览

The Fix

使用 $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);

The Cause

Angle-ui-router $http服务作为一个依赖项注入到 $TemplateFactory中,然后 $TemplateFactory在发送拦截器时在 $httpProvider内部创建一个对 $http的循环引用。

如果您试图像这样将 $http服务直接注入到拦截器中,则会引发相同的循环依赖异常。

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

关注点分离

循环依赖异常可能表明应用程序中存在混合的关注点,这可能导致稳定性问题。如果您发现自己处于这种异常状态,那么您应该花时间查看您的体系结构,以确保避免最终引用自己的任何依赖项。

@ Stephen Friedrich 的回答

我同意下面的回答,使用 $injector直接获得对所需服务的引用并不理想,可以认为是反模式。

发出一个事件是一个更优雅的解决方案,也是一个解耦的解决方案。

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

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

我在这里转发我的回答:

A Better Fix

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

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

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

还有

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

That way you have separated the concerns:

  1. 检测到401响应
  2. Redirect to login