将 $state (ui-router) 注入 $http 拦截器会导致循环依赖
Posted
技术标签:
【中文标题】将 $state (ui-router) 注入 $http 拦截器会导致循环依赖【英文标题】:Injecting $state (ui-router) into $http interceptor causes circular dependency 【发布时间】:2013-12-12 09:35:32 【问题描述】:我正在努力实现的目标
如果 $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);
【问题讨论】:
【参考方案1】:修复
使用$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
中,然后在调度拦截器时在 $httpProvider
自身内创建对 $http
的循环引用。
如果您尝试将$http
服务直接注入像这样的拦截器,则会引发相同的循环依赖异常。
var interceptor = ['$location', '$q', '$http', function($location, $q, $http)
关注点分离
循环依赖异常可能表明您的应用程序中存在混合问题,这可能会导致稳定性问题。如果您发现自己遇到此异常,您应该花时间查看您的架构,以确保避免任何最终引用自身的依赖项。
@Stephen Friedrich 的回答
我同意下面的答案,即使用$injector
直接获取对所需服务的引用并不理想,可以被视为反模式。
发出事件是一种更加优雅且解耦的解决方案。
【讨论】:
如何针对 Angular 1.3 进行调整,其中有 4 个不同的函数传递给拦截器? 用法是一样的,你将$injector
服务注入你的拦截器并调用$injector.get()
,你需要得到$state
服务。
我得到 $injectot.get("$state") as >,你能告诉我可能是什么问题吗?
当它是一个简单的情况时,这绝对是一个救命稻草,但是当你遇到这种情况时,这表明你确实在你的代码中的某个地方创建了一个循环依赖,这在 AngularJS 中很容易做到DI。 Misko Hevery 解释得很好in his blog;强烈建议您阅读它以改进您的代码。
如果您使用更高版本的 Angular,$httpProvider.responseInterceptors.push
将不再工作。请改用$httpProvider.interceptors.push()
。您还必须修改interceptor
。无论如何,感谢您的精彩回答! :)【参考方案2】:
问题与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');
);
);
这样你就分离了关注点:
-
检测 401 响应
重定向到登录
【讨论】:
实际上,这个问题是这个问题的重复,因为这个问题是在那个问题之前提出的。不过,喜欢你优雅的修复,所以请点赞。 ;-) 我不知道该放在哪里。$rootScope.$emit("unauthorized");【参考方案3】:在我尝试保存当前状态之前,Jonathan 的解决方案非常棒。在ui-router v0.2.10 中,当前状态似乎没有在拦截器的初始页面加载时填充。
无论如何,我通过使用$stateChangeError 事件来解决它。 $stateChangeError 事件为您提供 to 和 from 状态以及错误。挺好看的。
$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");
);
【讨论】:
我已经为此提交了issue。 我认为这对于 ngResource 是不可能的,因为它总是异步的?我尝试了一些东西,但我没有得到资源调用来防止状态更改...... 如果要拦截一个不触发状态改变的请求怎么办? 您可以使用与上述@Stephen Friedrich 相同的方法,实际上类似于此方法,但使用了自定义事件。以上是关于将 $state (ui-router) 注入 $http 拦截器会导致循环依赖的主要内容,如果未能解决你的问题,请参考以下文章
Angular ui-router 中 $state.transitionTo() 和 $state.go() 之间的区别
AngularJS:无法使用 ui-route $state 获取 html5 模式网址