Angular - Retry failed requests

We implement token authorization with updating the token logic in our application. In principle, everything works. But we want to repeat requests that fail due to expiration. Everything is done at Interceptor. Here is the code:

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

It seems that everything works fine when there is only one unsuccessful request, but with two, if I wait long enough (for example, overnight), I see that the retry gets into the loop. I am trying to update the refresh token with refreshTokenInProcess, but still see that every failed request receives a token update.

Give me some ideas / design patterns for this task.

thanks

+6
source share
1 answer

Here is a view of my compiled JavaScript, I used TypeScript and also provided this code. In general, I would suggest 2 additional templates:

  1. Save "refreshTokenInProcess" as a local storage variable in addition to the class variable. This will help maintain a single constant value to indicate whether an update is in progress.
  2. Track retryCount and maxRetryCount to make sure the loop is not happening. You can also perform another task if the number of repetitions is exceeded. After a successful update, retryCount is reset.

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; } } 
0
source

Source: https://habr.com/ru/post/1236622/


All Articles