【发布时间】:2015-07-27 04:05:02
【问题描述】:
我在控制器中使用 promise 在$http 请求之后呈现视图。一切正常,除非在 401 错误期间涉及 http 拦截器。当这种情况发生时,这就是我面临的问题:
http 拦截器从 api 获取 401 错误,添加 被拒绝的查询缓冲并显示登录模式。
登录成功后,队列中的api查询 401之前再次尝试,api返回请求的数据 成功了。
然而发生的情况是,即使数据是从 api 返回的, 角度视图未更新。视图仅在 手动刷新该状态。
这些是我的代码:
1) http拦截器:
我在这里使用http-auth-interceptor
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
return {
responseError: function(rejection) {
if (!rejection.config.ignoreAuthModule) {
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
angular.forEach(rejectionReasons, function(value, key) {
if(rejection.data.error === value) {
var deferred = $q.defer();
httpBuffer.append(rejection.config, deferred);
$rootScope.$broadcast('event:auth-loginRequired', rejection);
return deferred.promise;
}
})
}
// otherwise, default behaviour
return $q.reject(rejection);
}
};
}]);
2) http拦截器监听:
scope.$on('event:auth-loginRequired', function() {
LoginModalService.openLoginModal();
console.log('login needed');
});
scope.$on('event:auth-loginCancelled', function() {
$state.go('index');
});
3) 控制器:
.controller('FetchData', ['$scope', 'DataService',
function($scope, DataService) {
DataService.fetchNamesList()
.success(function(names) {
$scope.namesList = names;
}).error(function(error) {
// The login modal should show if auth error occurs
});
}])
这里,DataService.fetchNamesList() 执行来自 api 的获取请求。
如果针对上述情况触发了身份验证拦截器,我可以看到在成功登录后再次尝试 $http $_GET 但 $scope.namesList 未在视图中更新。
到目前为止我的想法:
1) 我正在考虑为上述内容添加一个额外的$watch,以便它在 http 拦截器之后工作。我想到了这一点,然后我放弃了该选项,因为如果我在一个页面中有多个获取请求(如仪表板),那么观察每个请求都会过火。
2) 登录后手动刷新 ui 路由器状态,如下所示。这可行,但当视图涉及表单提交时,它没有帮助,其中完成的表单将在状态重新加载后被重置:
$state.go($state.current, {}, {reload: true});
所以我确定如何解决这个问题。有人可以在这里指导我吗....
编辑:
在 @ChrisFoster 在 cmets 中的建议之后,我将 return deferred.promise 移到了 angular.forEach 之外,并稍微更改了 foreach 检查。这似乎在登录后正确更新了视图。但是,现在模式显示每个错误,而不仅仅是拒绝原因中列出的错误:
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
return {
responseError: function(rejection) {
if (!rejection.config.ignoreAuthModule) {
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
// Loop through each rejection reason and redirect
angular.forEach(rejectionReasons, function(value, key) {
if(!(rejection.data.error === value)) {
return $q.reject(rejection);
}
});
var deferred = $q.defer();
httpBuffer.append(rejection.config, deferred);
$rootScope.$broadcast('event:auth-loginRequired', rejection);
return deferred.promise;
}
// otherwise, default behaviour
return $q.reject(rejection);
}
};
}]);
【问题讨论】:
-
您的
return deferred.promise位于内部angular.forEach。这意味着它在该函数内部返回,而不是返回到 Promise 处理程序。您需要将其分配给forEach函数之外的变量并以这种方式返回。我不确定这是否是你的全部问题,但它是它的一部分:) -
@ChrisFoster 我认为你是对的。当我在
forEach循环之外删除return deferred.promise时,视图更新正常。所以你已经发现了我的错误。但是,我仍然坚持如何正确解决它。我的意思是,我稍微更改了 foreach,因此它会检查不等于并在不继续deferred.promise时返回默认行为。但是,当我将deferred.promise移到 foreach 之外时,模式会显示每 400 个错误,而不是我在拒绝原因下列出的错误。推迟和承诺的事情让我很困惑,我不知道是非。 -
我用我的错误尝试更新了我的问题。你能帮我吗?仍然坚持下去:(
-
@ChrisFoster 最后我让它工作了。我必须阅读基础知识并花一些时间学习
promise和angular forEach。后来我意识到我错了。我现在使用了一个简单的if语句并在其中处理deferred.promise。这似乎有效。我已将其添加为答案,因为这是我目前正在使用的解决方案。如果您觉得可以以更有效的方式完成,请告诉我。非常感谢您的意见。你50字的评论给了我丰富的知识。 :) -
很高兴你能成功!我很高兴你能弄清楚发生了什么。您当前的答案非常好,但如果您仍然想知道如何使用
angular.forEach来解决问题,请查看我在下面添加的答案 :)
标签: angularjs angularjs-scope angular-ui-router angular-services angular-http-interceptors