高级描述
我有一个使用 Google Oauth 的 React/redux/electron 应用程序。我希望能够在访问令牌过期时自动刷新访问令牌。我已经对此进行了研究,并使用中间件半成功地解决了它,但是我的解决方案在某些情况下出错了。
我已经实现了一个在每个 API 操作上运行的刷新中间件。它检查访问令牌是否已过期或即将过期。如果是这样,它不会调度它收到的操作,而是调度令牌刷新操作并将任何其他操作排队,直到收到新的访问令牌。之后,它会调度队列中的所有操作。
但是,我的一个动作创作者看起来像这样:
function queryThreads(params) {
return async (dispatch) => {
const threads = await dispatch(fetchThreads(params))
const newPageToken = threads.payload.nextPageToken
}
}
当刷新中间件由于令牌未过期而未运行时,将在此处定义threads.payload
,并且一切都将按预期工作。
但是,当刷新中间件运行时,threads.payload
将被undefined
,因为dispatch
似乎使用令牌刷新操作的值而不是fetchThreads
操作进行解析。
如何确保令牌被刷新(并在state
/localStorage
中更新(,fetchThreads
使用更新的令牌进行调度,并且threads
变量被分配给正确 Promise 的解析值?
指向项目代码的链接
这是我的刷新中间件。它的灵感来自kmmbvnr的这篇文章。
这是令牌刷新操作创建程序。
这是我的 queryThreads 操作创建器中的行,当令牌必须刷新时抛出(threads.payload
undefined
(。
这是我更新状态以响应令牌刷新的化简器。
这是我更新本地存储以响应令牌刷新的中间件。
看起来我已经通过像这样重写刷新中间件解决了这个问题:
function createRefreshMiddleware() {
const postponedRSAAs = [];
return ({ dispatch, getState }) => {
const rsaaMiddleware = apiMiddleware({ dispatch, getState });
return next => action => {
if (isRSAA(action)) {
try {
const auth = JSON.parse(localStorage.getItem('auth'));
const { refresh_token: refreshToken } = auth;
const expirationTime = jwtDecode(auth.id_token).exp * 1000;
const isAccessTokenExpiring =
moment(expirationTime) - moment() < 300000;
if (refreshToken && isAccessTokenExpiring) {
postponedRSAAs.push(action);
if (postponedRSAAs.length === 1) {
return rsaaMiddleware(next)(
dispatch(() => attemptTokenRefresh(refreshToken))
).then(() => {
const postponedRSAA = postponedRSAAs.pop();
return dispatch(postponedRSAA);
});
}
return rsaaMiddleware(next)(action);
}
return rsaaMiddleware(next)(action);
} catch (e) {
console.log(e);
return next(action);
}
}
return next(action);
};
};
}
export default createRefreshMiddleware();
现在,推迟的操作将始终与令牌刷新操作链接,因此我们没有原始承诺使用错误值解析的问题;而且它更简洁。