如何在Angular responseError拦截器中处理多个响应

时间:2017-07-28 22:58:05

标签: angularjs angular-http-interceptors refresh-token

我目前正在使用以下代码重新抛出从我的API返回401的请求:

responseError: function(rejection) {
                var authData = localStorageService.get('authorizationData');
                if (rejection.status === 401 && authData) {
                    var authService = $injector.get('authService');
                    var $http = $injector.get('$http');
                    ̶v̶a̶r̶ ̶d̶e̶f̶e̶r̶r̶e̶d̶ ̶=̶ ̶$̶q̶.̶d̶e̶f̶e̶r̶(̶)̶;̶ 

                    var promise = authService.refreshToken();

                    return  ̶d̶e̶f̶e̶r̶r̶e̶d̶.̶ promise.then(function () {
                        return $http(rejection.config);
                    });
                }
                return $q.reject(rejection);
            }

这适用于1个请求,但如果我从单个页面返回两个401,它似乎无法工作,例如当页面加载两个api调用以填充不同的部分时。如何让我的拦截器重新抛出多个延期调用?

另外,不应该为每个401单独拦截拦截器吗?不理想,它会在单个页面上引起多次刷新调用,但由于调用没有被重新抛出而导致数据丢失的改进。

截图:

enter image description here

2 个答案:

答案 0 :(得分:1)

一种方法是保存令牌保证并链接第二次和后续重试,直到令牌刷新完成:

responseError: function(rejection) {
    var authService = $injector.get('authService');
    var $http = $injector.get('$http');
    var tokenPromise = null;

    var authData = localStorageService.get('authorizationData');
    if (rejection.status === 401 && authData) {

        if (!tokenPromise) {
            tokenPromise = authService.refreshToken()
              .finally(function() {
                tokenPromise = null;
            });
        };  

        return tokenPromise.then(function () {
            return $http(rejection.config);
        });
    } else {
        throw rejection;
    }
}

在上面的示例中,拒绝处理程序创建一个令牌刷新承诺,然后在令牌刷新结束(满足或拒绝)时使其为空。如果在令牌刷新过程中发生另一次拒绝,则以链接(并延迟)重试,直到令牌刷新XHR完成。

答案 1 :(得分:1)

非常相似的georgeawg答案......

responseError: function(rejection) {
                var authData = localStorageService.get('authorizationData');
                if (rejection.status === 401 && authData && !isAuthRequest() /* If request for refresh token fails itself do not go into loop, i.e. check by url */) {
                    var authService = $injector.get('authService');
                    var $http = $injector.get('$http');

                    var promise = authService.refreshTokenExt(); // look below

                    return  ̶promise.then(function () {
                        return $http(rejection.config);
                    });
                }
                return $q.reject(rejection);
            }

AuthService:

...
var refreshAuthPromise;

service.refreshTokenExt = function() {
  if (refreshAuthPromise == null) {
    refreshAuthPromise = authService.refreshToken().catch(function() {
      // Cant refresh - redirect to login, show error or whatever
    }).finally(function() {
      refreshAuthPromise = null;
    });
  }
  return refreshAuthPromise;
}