What I'm trying to achieve
我想要达到的目标
I would like to to transition to a certain state (login) in case an $http request returns a 401 error. I have therefore created an $http interceptor.
如果$http请求返回401错误,我希望转换到某个状态(登录)。因此,我创建了一个$http拦截器。
The problem
这个问题
When I am trying to insert '$state' into the interceptor I get a circular dependency. Why and how do i fix it?
当我试图向拦截器插入“$state”时,我得到了一个循环依赖项。为什么,我如何修复它?
Code
代码
//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 个解决方案
#1
209
The Fix
Use the $injector
service to get a reference to the $state
service.
使用$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
angular-ui-router injects the $http
service as a dependency into $TemplateFactory
which then creates a circular reference to $http
within the $httpProvider
itself upon dispatching the interceptor.
angular-ui-router将$http服务作为依赖项注入到$TemplateFactory中,然后在分配拦截器时,在$httpProvider本身中创建对$http的循环引用。
The same circular dependency exception would be thrown if you attempt to inject the $http
service directly into an interceptor like so.
如果您试图将$http服务直接注入到像这样的拦截器中,那么将抛出相同的循环依赖异常。
var interceptor = ['$location', '$q', '$http', function($location, $q, $http) {
Separation of Concerns
Circular dependency exceptions can indicate that there is a mixing of concerns within your application which could cause stability issues. If you find yourself with this exception you should take the time to look at your architecture to ensure you avoid any dependencies that end up referencing themselves.
循环依赖异常可以表明您的应用程序中存在混合的问题,这可能导致稳定性问题。如果您发现自己有这个异常,您应该花时间查看您的体系结构,以确保避免最终引用它们自己的任何依赖关系。
@Stephen Friedrich's answer
I agree with the answer below that using the $injector
to directly get a reference to the desired service is not ideal and could be considered an anti pattern.
我同意下面的答案,使用$injector直接获取所需服务的引用不是理想的,可以视为反模式。
Emitting an event is a much more elegant and also decoupled solution.
发送事件是一种更加优雅的解决方案,也是一种解耦的解决方案。
#2
24
The question is a duplicate of AngularJS: Injecting service into a HTTP interceptor (Circular dependency)
问题是AngularJS的重复:将服务注入HTTP拦截器(循环依赖)
I am re-posting my answer from that thread here:
我把我的答案重新贴在这里:
A Better Fix
I think using the $injector directly is an antipattern.
我认为直接使用注入器是一个反模式。
A way to break the circular dependency is to use an event: Instead of injecting $state, inject $rootScope. Instead of redirecting directly, do
打破循环依赖关系的一种方法是使用一个事件:不是注入$state,而是注入$rootScope。与其直接重定向,不如直接重定向
this.$rootScope.$emit("unauthorized");
plus
+
angular
.module('foo')
.run(function($rootScope, $state) {
$rootScope.$on('unauthorized', () => {
$state.transitionTo('login');
});
});
That way you have separated the concerns:
这样你就把关注点分开了:
- Detect a 401 response
- 检测401响应
- Redirect to login
- 重定向到登录
#3
16
Jonathan's solution was great until I tried to save the current state. In ui-router v0.2.10 the current state does not seem to be populated on initial page load in the interceptor.
乔纳森的解决方案很好,直到我试图挽救目前的状态。在ui-router v0.2.10中,当前状态似乎没有在拦截器中的初始页负载上填充。
Anyway, I solved it by using the $stateChangeError event instead. The $stateChangeError event gives you both to and from states, as well as the error. It's pretty nifty.
不管怎样,我用$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");
}
});
#1
209
The Fix
Use the $injector
service to get a reference to the $state
service.
使用$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
angular-ui-router injects the $http
service as a dependency into $TemplateFactory
which then creates a circular reference to $http
within the $httpProvider
itself upon dispatching the interceptor.
angular-ui-router将$http服务作为依赖项注入到$TemplateFactory中,然后在分配拦截器时,在$httpProvider本身中创建对$http的循环引用。
The same circular dependency exception would be thrown if you attempt to inject the $http
service directly into an interceptor like so.
如果您试图将$http服务直接注入到像这样的拦截器中,那么将抛出相同的循环依赖异常。
var interceptor = ['$location', '$q', '$http', function($location, $q, $http) {
Separation of Concerns
Circular dependency exceptions can indicate that there is a mixing of concerns within your application which could cause stability issues. If you find yourself with this exception you should take the time to look at your architecture to ensure you avoid any dependencies that end up referencing themselves.
循环依赖异常可以表明您的应用程序中存在混合的问题,这可能导致稳定性问题。如果您发现自己有这个异常,您应该花时间查看您的体系结构,以确保避免最终引用它们自己的任何依赖关系。
@Stephen Friedrich's answer
I agree with the answer below that using the $injector
to directly get a reference to the desired service is not ideal and could be considered an anti pattern.
我同意下面的答案,使用$injector直接获取所需服务的引用不是理想的,可以视为反模式。
Emitting an event is a much more elegant and also decoupled solution.
发送事件是一种更加优雅的解决方案,也是一种解耦的解决方案。
#2
24
The question is a duplicate of AngularJS: Injecting service into a HTTP interceptor (Circular dependency)
问题是AngularJS的重复:将服务注入HTTP拦截器(循环依赖)
I am re-posting my answer from that thread here:
我把我的答案重新贴在这里:
A Better Fix
I think using the $injector directly is an antipattern.
我认为直接使用注入器是一个反模式。
A way to break the circular dependency is to use an event: Instead of injecting $state, inject $rootScope. Instead of redirecting directly, do
打破循环依赖关系的一种方法是使用一个事件:不是注入$state,而是注入$rootScope。与其直接重定向,不如直接重定向
this.$rootScope.$emit("unauthorized");
plus
+
angular
.module('foo')
.run(function($rootScope, $state) {
$rootScope.$on('unauthorized', () => {
$state.transitionTo('login');
});
});
That way you have separated the concerns:
这样你就把关注点分开了:
- Detect a 401 response
- 检测401响应
- Redirect to login
- 重定向到登录
#3
16
Jonathan's solution was great until I tried to save the current state. In ui-router v0.2.10 the current state does not seem to be populated on initial page load in the interceptor.
乔纳森的解决方案很好,直到我试图挽救目前的状态。在ui-router v0.2.10中,当前状态似乎没有在拦截器中的初始页负载上填充。
Anyway, I solved it by using the $stateChangeError event instead. The $stateChangeError event gives you both to and from states, as well as the error. It's pretty nifty.
不管怎样,我用$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");
}
});