4

高级描述

我有一个使用 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.payloadis undefined)。

这是我更新状态以响应令牌刷新的减速器。

这是我更新 localStorage以响应令牌刷新的中间件。

4

1 回答 1

2

看起来我已经通过像这样重写刷新中间件解决了这个问题:

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

现在推迟的动作将始终与令牌刷新动作链接,因此我们不存在原始承诺以错误值解决的问题;加上它更简洁。

于 2018-09-12T19:38:14.693 回答