在http拦截器

时间:2015-07-26 07:21:43

标签: angularjs angularjs-scope angular-ui-router angular-services angular-http-interceptors

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

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

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

  3. 然而,即使从api返回数据,会发生什么 角度视图未更新。视图仅在更新后更新 手动刷新该状态。

  4. 这些是我的代码:

    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);
            }
          };
        }]);
    

2 个答案:

答案 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函数,不是中返回该值。因此,您有三种选择:

  1. 您可以使用传统的javascript forEach循环
  2. 您可以使用for语句条件
  3. 您可以使用&#34;外部&#34;变量你跟踪
  4. 您的示例正在使用第二种方法,但如果您仍想使用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之外的变量   功能并以这种方式返回。