我在控制器中使用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收到请求。
如果针对上述内容触发了auth拦截器,我可以看到成功登录后再次尝试$http $_GET
,但$scope.namesList
未在视图中更新。
到目前为止我的想法:
1)我正在考虑为上面添加一个额外的$watch
,以便它能够在http拦截器之后工作。我想到了那个然后我放弃了那个选项,因为如果我在一个页面(如仪表板)中有多个获取请求,那么观看每个请求都可能会过火。
2)如下登录后手动刷新ui路由器状态。这有效,但是当视图涉及表单提交时,它将无效,其中完成的表单将在状态重新加载后重置:
$state.go($state.current, {}, {reload: true});
所以我相信如何解决这个问题。有人可以在这里指导我....
修改
在@ChrisFoster的评论建议之后,我将return deferred.promise
移到了angular.forEach
之外,稍微更改了foreach检查。这似乎在登录后正确更新了视图。但是,现在模式显示的是每个错误,而不仅仅是rejectReasons中列出的错误:
$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);
}
};
}]);
答案 0 :(得分:3)
很高兴看到你的答案正常。如果你愿意,你仍然可以使用// $result = $conn->query($sql);
$i = 0;
$all = array();
while($row = $result->fetch_assoc()) {
$i++;
$arr[$i]['id'] = $row['id'];
$arr[$i]['firstname'] = $row['first_name'];
$arr[$i]['lastname'] = $row['last_name'];
$all[] = $arr[$i];
}
echo '<pre>';
echo print_r($all);
,这里有一个示例:
angular.forEach
您遇到$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
return {
responseError: function(rejection) {
var loginRequired = false;
var rejectionReasons = [
'token_not_provided', 'token_expired',
'token_absent', 'token_invalid'
];
// Loop through each rejection reason
angular.forEach(rejectionReasons, function(value, key) {
if (!(rejection.data.error === value)) {
// Set loginRequired, and check this later
// We *can't* return here because we are inside a function :)
loginRequired = true;
}
});
// Create a promise
var deferred = $q.defer();
if (loginRequired && !rejection.config.ignoreAuthModule) {
// Display a login module and queue a retry request
httpBuffer.append(rejection.config, deferred);
$rootScope.$broadcast('event:auth-loginRequired', rejection);
} else {
// Not a auth error, or ignoreAuthModule is enabled
// Therefore immediately reject the promise with the fail value
deferred.reject(rejection);
}
// Return the promise
return deferred.promise;
}
};
}]);
问题的原因是您将函数作为参数传递给forEach
。当你在forEach
内部时,你会在HTTP拦截器函数中的return
函数,不是中返回该值。因此,您有三种选择:
forEach
循环for
语句条件您的示例正在使用第二种方法,但如果您仍想使用if
,则上面的代码是第三种方法的演示。第一种方法也是可行的,但稍微不那么常见,而且更麻烦。
答案 1 :(得分:0)
这就是我正在使用的工作:
1)删除angular.forEach
并用简单的if
语句替换它。由于我只是检查了一些条件,if
并不是我认为的另一种选择。
2)我在deferred.promise
语句中添加了if
,之后一切正常。登录后视图已更新,控制器无需继续解析从api返回的数据。
这是我的代码:
$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
if (
rejection.data.error === 'token_not_provided' ||
rejection.data.error === 'token_expired' ||
rejection.data.error === 'token_absent' ||
rejection.data.error === 'token_invalid'
)
{
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);
}
};
}]);
感谢@ChrisFoster指出了这个错误:
你的return deferred.promise在angular.forEach中。这意味着 它在该函数内部返回,而不是在promise处理程序中返回。 您需要将其分配给forEach之外的变量 功能并以这种方式返回。