javascript - Angular - 重试失败的请求

标签 javascript angularjs token

我们正在使用我们的应用程序的刷新 token 逻辑实现 token 授权。基本上,一切正常。但是我们想重试由于 token 过期而失败的请求。一切都在拦截器中完成。这是一些相关代码:

a.service('APIInterceptor', function ($q, $rootScope, $location, $window,    $injector) {
var service = this;
var $http;
var refreshTokenInProcess = false;

executeRequest = function (config) {
    var accessToken = $window.localStorage.getItem('token');
    if (accessToken != 'null') {
        config.headers.authorization = "bearer " + accessToken;
    }
    lastRequest = config;
    return config;
};
service.request = function (config) {
    return executeRequest(config);
};
var tokenRefreshing = function () {
    var deferred = $q.defer();
    // Run refresh token service only once in case multiple requests are failing
    if (refreshTokenInProcess == false) {
        var refreshToken = $window.localStorage.getItem('refresh_token');
        var clientId = $window.localStorage.getItem('client_id');
        var apiUrl = $window.localStorage.getItem('apiUrl');

        var param = "grant_type=refresh_token&refresh_token=" + refreshToken + "&client_id=" + clientId;
        $http = $http || $injector.get('$http');
        $http.post(apiUrl + 'token', param, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).
        then(function (success) {
            $window.localStorage.setItem('token', success.data.access_token);
            $window.localStorage.setItem('refresh_token', success.data.refresh_token);
            $window.localStorage.setItem('client_id', "web");
            $window.localStorage.setItem('expires', success.data[".expires"]);
            deferred.resolve(success);
            refreshTokenInProcess = false;
        }, function (err) {
            deferred.reject(err);
        });
    }
    else
        deferred.resolve();
    refreshTokenInProcess = true;
    return deferred.promise;
};
service.responseError = function (response) {
    if (response.status === 406 && response.data === "Unauthenticated Token.") {
            //retry logic
            tokenRefreshing().then(function () {
                return $http(executeRequest(response.config)).then(function (data) {
                    if (data)
                        response.config.callerController(data.data);
                })
            });
    }
};

当只有一个失败的请求时,一切似乎都正常,但如果我等待足够长的时间(比如一夜之间),我会发现重试进入了一个循环。我正在尝试使用 refreshTokenInProcess 标记 token 刷新,但仍然看到每个失败的请求都获得了 token 刷新。

请给我一些关于这项任务的想法/设计模式。

谢谢

最佳答案

这是我编译的 JavaScript 的 View ,我使用 TypeScript 并提供了该代码。一般来说,我会建议另外两种模式:

  1. 将“refreshTokenInProcess”存储为除类变量之外的本地存储变量。这将有助于保留一个单一的持久值以指示刷新是否正在进行中。
  2. 跟踪 retryCount 和 maxRetryCount 以确保不会发生循环。如果超过重试次数,您还可以执行其他一些任务。成功刷新后,retryCount 将重置。

JavaScript

var TestServices = /** @class */ (function () {
    function TestServices($window, $injector, $http, $q) {
        var _this = this;
        this.$window = $window;
        this.$injector = $injector;
        this.$http = $http;
        this.$q = $q;
        this.tokenRefreshing = function () {
            var deferred = _this.$q.defer();
            // Run refresh token service only once in case multiple requests are failing
            _this.retryCount++;
            var refreshToken = _this.$window.localStorage.getItem('refresh_token');
            var clientId = _this.$window.localStorage.getItem('client_id');
            var apiUrl = _this.$window.localStorage.getItem('apiUrl');
            var param = 'grant_type=refresh_token&refresh_token=' + refreshToken + '&client_id=' + clientId;
            _this.$http = _this.$http || _this.$injector.get('$http');
            _this.$http.post(apiUrl + 'token', param, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).
                then(function (success) {
                _this.$window.localStorage.setItem('token', success.data.access_token);
                _this.$window.localStorage.setItem('refresh_token', success.data.refresh_token);
                _this.$window.localStorage.setItem('client_id', 'web');
                _this.$window.localStorage.setItem('expires', success.data['.expires']);
                _this.refreshTokenInProcess = false;
                // reset the retry count
                _this.retryCount = 0;
                deferred.resolve(success);
            }, function (err) {
                _this.refreshTokenInProcess = false;
                deferred.reject(err);
            });
            return deferred.promise;
        };
    }
    Object.defineProperty(TestServices.prototype, "refreshTokenInProcess", {
        get: function () {
            if (this.$window) {
                this._refreshTokenInProcess = this.$window.localStorage.getItem('refreshTokenInProcess') === 'true';
            }
            return this._refreshTokenInProcess === true;
        },
        set: function (value) {
            this._refreshTokenInProcess = value === true;
            var strValue = value === true ? 'true' : 'false';
            if (this.$window) {
                this.$window.localStorage.setItem('refreshTokenInProcess', strValue);
            }
        },
        enumerable: true,
        configurable: true
    });
    TestServices.prototype.executeRequest = function (config) {
        var accessToken = this.$window.localStorage.getItem('token');
        if (accessToken !== 'null') {
            config.headers.authorization = 'bearer ' + accessToken;
        }
        this.lastRequest = config;
        return config;
    };
    TestServices.prototype.request = function (config) {
        return this.executeRequest(config);
    };
    TestServices.prototype.responseError = function (response) {
        var _this = this;
        if (response.status === 406 && response.data === 'Unauthenticated Token.') {
            // retry logic
            if (this.refreshTokenInProcess === false && this.retryCount < this.maxRetryCount) {
                this.refreshTokenInProcess = true;
                this.tokenRefreshing().then(function () {
                    return _this.$http(_this.executeRequest(response.config)).then(function (data) {
                        if (data) {
                            response.config.callerController(data.data);
                        }
                    });
                });
            }
        }
        return response;
    };
    return TestServices;
}());

typescript

export class TestServices implements ng.IHttpInterceptor {

    private _refreshTokenInProcess: boolean;

    get refreshTokenInProcess(): boolean {

        if (this.$window) {
            this._refreshTokenInProcess = this.$window.localStorage.getItem('refreshTokenInProcess') === 'true';
        }
        return this._refreshTokenInProcess === true;
    }
    set refreshTokenInProcess(value: boolean) {
        this._refreshTokenInProcess = value === true;
        const strValue = value === true ? 'true' : 'false';
        if (this.$window) {
            this.$window.localStorage.setItem('refreshTokenInProcess', strValue);
        }
    }

    lastRequest: any;
    maxRetryCount: 10;
    retryCount: 0;

    constructor(public $window: ng.IWindowService, public $injector: ng.auto.IInjectorService, public $http: ng.IHttpService, public $q: ng.IQService) {

    }

    executeRequest(config: ng.IRequestConfig) {
        const accessToken = this.$window.localStorage.getItem('token');
        if (accessToken !== 'null') {
            config.headers.authorization = 'bearer ' + accessToken;
        }
        this.lastRequest = config;
        return config;
    }

    request(config: ng.IRequestConfig) {
        return this.executeRequest(config);
    }

    tokenRefreshing = () => {
        const deferred = this.$q.defer();
        // Run refresh token service only once in case multiple requests are failing
        this.retryCount++;
        const refreshToken = this.$window.localStorage.getItem('refresh_token');
        const clientId = this.$window.localStorage.getItem('client_id');
        const apiUrl = this.$window.localStorage.getItem('apiUrl');

        const param = 'grant_type=refresh_token&refresh_token=' + refreshToken + '&client_id=' + clientId;
        this.$http = this.$http || this.$injector.get('$http');
        this.$http.post(apiUrl + 'token', param, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).
            then((success: any) => {
                this.$window.localStorage.setItem('token', success.data.access_token);
                this.$window.localStorage.setItem('refresh_token', success.data.refresh_token);
                this.$window.localStorage.setItem('client_id', 'web');
                this.$window.localStorage.setItem('expires', success.data['.expires']);
                this.refreshTokenInProcess = false;
                // reset the retry count
                this.retryCount = 0;
                deferred.resolve(success);
            }, (err: any) => {
                this.refreshTokenInProcess = false;
                deferred.reject(err);
            });
        return deferred.promise;
    }

    responseError(response: any) {
        if (response.status === 406 && response.data === 'Unauthenticated Token.') {
            // retry logic
            if (this.refreshTokenInProcess === false && this.retryCount < this.maxRetryCount) {
                this.refreshTokenInProcess = true;
                this.tokenRefreshing().then(() => {
                    return this.$http(this.executeRequest(response.config)).then((data: any) => {
                        if (data) {
                            response.config.callerController(data.data);
                        }
                    });
                });
            }
        }
        return response;
    }

}

关于javascript - Angular - 重试失败的请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33873465/

相关文章:

javascript - 显示/隐藏具有不同类的 li 元素

javascript - 如何在 PhoneGap 的 windows 平台上实现 map (Google 或 Bing)?

javascript - 如何从第一个表单传递id和名称,并在angularjs中仅显示第二个表单中的名称

java - 了解 ANTLR4 代币

elasticsearch - 如何在 ElasticSearch 中不分析?

javascript - 通过 POST 转到另一个页面

javascript - THREE.js - 将粒子均匀地放置在对象的面上而不是顶点上

javascript - React 组件中的 AngularJS 过滤器

javascript - 如何过滤表中的二级ng-repeat

c - 编译器中的标记数