与承诺和角度拦截器混淆

时间:2015-05-18 21:25:59

标签: angularjs rest token interceptor

我正在为基于令牌的restful API编写angularjs客户端。 API中的令牌每小时到期,因此每次令牌在我的客户端过期时,都应该有刷新令牌操作。

处理API调用结果的控制器如下所示:

angular.module('apitestApp')
  .controller('MainCtrl', ['$rootScope', '$scope', 'httpService', function ($rootScope, $scope, httpService) {
    $scope.messages = [];
    var url = $rootScope.domainPath + $rootScope.apiPath + 'messages.json';
    httpService.getRequest(url, {}).then(
      function (data){
          $scope.messages = data;
      }
    );
  }]);

我有一个使用angularjs $ resource

进行API调用的服务
angular.module('apitestApp')
  .service('httpService', ['$rootScope', '$resource', '$localStorage', function ($rootScope, $resource, $localStorage) {
    this.getRequest = function (url, params){
        var res = $resource(url, params, {
            query: {
                method: 'GET',
                isArray: true,
                headers: { 'Authorization': 'Bearer ' +  $localStorage.token.access_token }
            }
        });
        return res.query().$promise;
    };
    this.refreshToken = function (){
        var url = $rootScope.domainPath + this.authPath;
        var request = $resource(url);
        return request.get({
            client_id: this.clientId,
            client_secret: this.secret,
            grant_type: 'refresh_token',
            refresh_token: $localStorage.token.refresh_token
            },
            function (data){
                $localStorage.token = data;
            }
        ).$promise;
    };
  }]);

最后是一个处理所有未授权请求的拦截器(401),刷新访问令牌并重试失败的请求。

angular.module('apitestApp')
 .factory('apiInterceptor', ['$q', '$injector', function ($q, $injector){
    //Handling error codes
    return {
        response : function (response){
            return response || $q.when(response);
        },
        responseError: function (rejection){
            switch(rejection.status){
                case 400:
                    console.log("Bad request");
                    break;
                case 401:
                    var config = rejection.config;
                    var deferred = $q.defer();
                    var httpService = $injector.get('httpService');
                    httpService.refreshToken().then(deferred.resolve, deferred.reject);
                    return deferred.promise.then(function (){
                        return httpService.getRequest(config.url, config.params);
                    });
                    //break;
                case 500:
                    console.log("Internal server error");
                    break;
                default:
                    console.log("Another error");
                    break;
            }
            return $q.reject(rejection);
        }
    };
  }]);

当访问令牌有效时,我服务中的 getRequest()方法成功返回一个promise,这与我希望拦截器返回但是不一样。如果访问令牌已过期,拦截器会捕获401错误,然后更新访问令牌并最终发出相同的请求,问题是我的控制器没有得到任何响应。

如何执行刷新令牌操作并代表用户返回预期数据?我在拦截器中做错了什么?

1 个答案:

答案 0 :(得分:2)

您想要从控制器中删除$ rootScope提供程序,这不是Angular的最佳做法,因为控制器在$ rootScope中拥有自己的范围。服务和工厂可以放在$ rootScope上,因为它不会创建它自己的范围,而是他们会监听自己的事件。

此外,将任何异步活动/ HTTP调用放入服务/工厂是最佳做法。只记得"瘦小的控制者,胖子服务"。

也许尝试使用一种使用某种发布/订阅设计的异步处理程序。现在,如果失败,一旦getRequest函数完成异步,它将调用更新消息的存储值,触发对订阅该方法的任何控制器的范围摘要的更新:

控制器

- (IBAction)save:(id)sender {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docDir = [paths objectAtIndex:0];
    NSString *fullFileName = [NSString stringWithFormat:@"%@/temporaryArray", docDir];
    [NSKeyedArchiver archiveRootObject:names toFile:fullFileName];
}

服务

angular.module('apitestApp')
  .controller('MainCtrl', ['$scope', 'httpService', function ($scope, httpService) {
    $scope.messages = [];
    httpService.setPath();
    httpService.onMessageReady($scope, function (messagesData) {
      $scope.messages = messagesData;
    });
  }]);
相关问题