首页 文章

在刷新访问令牌时,如何在react-redux应用程序中暂停和重新启动API调用?

提问于
浏览
3

我们有一个react-redux应用程序,可以在每次加载页面时使用多个API调用来获取数据 . 该应用程序遵循OAuth2协议 . 它有一个频繁到期的访问令牌和一个用于获取新访问令牌的刷新令牌 . 如果使用过期的访问令牌进行API调用,则会收到401错误,并显示错误消息“API令牌已过期” . 然后我们需要从auth服务器获取一个新令牌 .

我的问题是这样的:当页面加载时,说调用了8个API调用 . 我们收到3个成功的200个,但从第4个响应开始,我们收到401个“API令牌已过期” . 此时,我想放置我已经进行的所有API调用,但在刷新访问令牌之前没有收到响应或在队列中收到401错误 . 成功刷新访问令牌后,我想重新执行队列中保存的API调用 . 我怎样才能做到这一点?

在网上寻找这个,我发现redux-saga可能有用,但没有看到任何迹象表明它可以用于这个用例 .

1 回答

  • 3

    我也常常处理这个案子 . 这是我的解决方案:

    /**
     * Connect to API
     */
    export const makeRequest = (args) => {
        const request = fetch(args)//example
    
        return _retryRequestIfExpired(request, args)
    }
    
    
    /**
     * Fake get access token.
     */
    const _getNewAccessToken = () => {
        return new Promise((resolve, reject) => {
            resolve('xyz')
        })
    }
    
    const _retryRequestIfExpired = (request, args) => {
        return request.catch(error => {
            if (error === 'abc') {//Any reason you want
                return _refreshAccessToken()
                    .then(newAccessToken => {
                        const updateArgs = {
                            ...args,
                            headers: {
                                'Authorization': newAccessToken
                            }
                        }
    
                        //Retry with new access token
                        return makeRequest(updateArgs)
                    })
            }
    
            throw error
        })
    }
    
    /**
     * Important
     */
    let _isRefreshingToken = false
    let _subscribers = []
    
    const _subscribe = subscriber => {
        if (typeof subscriber !== 'function' || _subscribers.indexOf(subscriber) !== -1) {
            return false
        }
    
        _subscribers = [].concat(_subscribers, [subscriber])
    }
    
    const _broadcast = (error = null, data) => {
        _isRefreshingToken = false
    
        _subscribers.forEach(subscriber => {
            subscriber(error, data)
        })
    
        _subscribers = []
    }
    
    const _refreshAccessToken = () => {
        if (_isRefreshingToken) {//If a request is creating new access token
            return new Promise((resolve, reject) => {
                const subscriber = (error, accessToken) => {
                    if (error) {
                        return reject(error)
                    }
    
                    return resolve(accessToken)
                }
    
                _subscribe(subscriber)
            })
        }
    
        _isRefreshingToken = true
    
        return _getNewAccessToken()
            .then(accessToken => {
                _broadcast(null, accessToken)
    
                return accessToken
            })
            .catch(error => {
                _broadcast(error)
    
                throw error
            })
    }
    /**
     * End Important
     */
    

    这样,只有第一个请求才会实际创建一个新的访问令牌,其余的请求将暂时停止,直到创建一个新的访问令牌 .

相关问题