jjzjj

javascript - Angular - 重试失败的请求

coder 2025-01-20 原文

我们正在使用我们的应用程序的刷新 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 - Angular - 重试失败的请求的更多相关文章

  1. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  2. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  3. jquery - 我的 jquery AJAX POST 请求无需发送 Authenticity Token (Rails) - 2

    rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送

  4. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

  5. ruby - 正则表达式在哪个位置失败? - 2

    我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束

  6. ruby - 使用 rbenv 和 ruby​​-build 构建 ruby​​ 失败,出现 undefined symbol : SSLv2_method - 2

    我正在尝试在配备ARMv7处理器的SynologyDS215j上安装ruby​​2.2.4或2.3.0。我用了optware-ng安装gcc、make、openssl、openssl-dev和zlib。我根据README中的说明安装了rbenv(版本1.0.0-19-g29b4da7)和ruby​​-build插件。.这些是随optware-ng安装的软件包及其版本binutils-2.25.1-1gcc-5.3.0-6gconv-modules-2.21-3glibc-opt-2.21-4libc-dev-2.21-1libgmp-6.0.0a-1libmpc-1.0.2-1libm

  7. ruby-on-rails - Ruby 的 'open_uri' 是否在读取或失败后可靠地关闭套接字? - 2

    一段时间以来,我一直在使用open_uri下拉ftp路径作为数据源,但突然发现我几乎连续不断地收到“530抱歉,允许的最大客户端数(95)已经连接。”我不确定我的代码是否有问题,或者是否是其他人在访问服务器,不幸的是,我无法真正确定谁有问题。本质上,我正在读取FTPURI:defself.read_uri(uri)beginuri=open(uri).readuri=="Error"?nil:urirescueOpenURI::HTTPErrornilendend我猜我需要在这里添加一些额外的错误处理代码...我想确保我采取一切预防措施来关闭所有连接,这样我的连接就不是问题所在,但是我

  8. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  9. ruby-on-rails - Ruby 流量控制 : throw an exception, 返回 nil 还是让它失败? - 2

    我在思考流量控制的最佳实践。我应该走哪条路?1)不要检查任何东西并让程序失败(更清晰的代码,自然的错误消息):defself.fetch(feed_id)feed=Feed.find(feed_id)feed.fetchend2)通过返回nil静默失败(但是,“CleanCode”说,你永远不应该返回null):defself.fetch(feed_id)returnunlessfeed_idfeed=Feed.find(feed_id)returnunlessfeedfeed.fetchend3)抛出异常(因为不按id查找feed是异常的):defself.fetch(feed_id

  10. ruby - HTTP 请求中的用户代理,Ruby - 2

    我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)

随机推荐