2

我在控制器中使用 promise 在$http请求后呈现视图。一切正常,除非在 401 错误期间涉及 http 拦截器。发生这种情况时,这就是我面临的问题:

  1. http 拦截器从 api 获取 401 错误,将被拒绝的查询添加到缓冲区并显示登录模式。

  2. 登录成功后,再次尝试之前有401的排队api查询,api成功返回请求的数据。

  3. 然而发生的情况是,即使从 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 在评论中提出建议后,我移动了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);
        }
      };
    }]);
4

2 回答 2

3

很高兴看到您的回答有效。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. 当您return进入其中时,您将forEach函数中返回该值,而不是在 HTTP 拦截器函数中。因此,您有三个选择:

  1. 您可以使用传统的 javascriptfor循环
  2. 您可以使用if条件语句
  3. 您可以使用您跟踪的“外部”变量

您的示例使用的是第二种方法,但如果您仍想使用angular.forEach我上面的代码,则演示了第三种方法。第一种方法也是可能的,但稍微不那么常见并且有点混乱。

于 2015-07-26T20:05:02.643 回答
0

这就是我目前正在使用的工作:

1)删除angular.forEach并用一个简单的if语句替换它。由于我只检查几个条件,if所以我认为的替代方案还不错。

2)我deferred.promiseif声明中添加,之后一切正常。登录后视图得到更新,控制器可以继续解析从 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 指出错误:

您的返回 deferred.promise 在 angular.forEach 内。这意味着它在该函数内部返回,而不是返回到 Promise 处理程序。您需要将其分配给 forEach 函数之外的变量并以这种方式返回。

于 2015-07-26T17:38:33.110 回答