为过期的会话创建Redux中间件重试功能

发布时间:2020-07-07 17:22

在我的应用中,我正在通过api中间件运行每个请求。当使用Authorization的请求发生且令牌已过期时,我正在尝试创建中间件重试功能。这是我当前的API中间件:

const apiMiddleware = ({ dispatch }) => next => action => {
  next(action);
  // creating request data and params

  const retryRequest = () => {
    // refresh tokens with method: dispatch(getTokens());
    // retry initial request
  };

  axios({
    method,
    url,
    headers,
    [dataOrParams]: data,
  })
    .then(({ data: apiData }) => {
      dispatch(onSuccess(apiData));
    })
    .catch(error => {
      if (withToken && error.response.status === 401) {
        retryRequest();
      }
      return dispatch(apiError(label, error));
    })
};

export default apiMiddleware;

当我调用retryRequest()方法时,getTokens()请求启动,但同时启动了初始请求,并且Redux尚未使用新的刷新令牌进行更新,并且请求再次失败,因为getTokens()尚未完成。

我知道我做错了方法,我还能尝试其他解决方案吗?因此,首先调用并完成请求getTokens()才能完成初始请求。

回答1

如果可以使retryRequest()和catch函数异步,则可以使用

if(withToken && error.reponse.status === 401)
  await retryRequest();
return dispatch(apiError(label, error));

如果不能,则从重试请求中返回承诺,然后

if(withToken && error.response.status === 401)
  return retryRequest().then(_=> dispatch(apiError(label, error)))
return dispatch(apiError(label, error))

您还可以使用axios拦截器,这是我在最近的项目中使用的那个

api.interceptors.response.use(
  function (response) {
    response.data = parseResponseData(response.data);
    return response;
  },
  async function (error) {
    if (!error.response)
      Ant.message.error('Não foi possivel se conectar com o servidor');
    else if (
      error.response.status === 500 &&
      window.location.pathname !== '/erro/500'
    ) {
      if ((error.config.method as string).toLowerCase() === 'get')
        navigate('/erro/500');
      else
        Ant.message.error(
          'Desculpe, parece que algo deu errado no servidor.',
        );
    } else if (error.response.status === 401) {
      let request = error.config;
      const auth = localStorage.getItem('auth');
      let refreshToken = auth && JSON.parse(auth)['refreshToken'];
      var authService = new AuthService();
      return await authService
        .refresh(refreshToken)
        .then(async (resp) => {
          store.dispatch(login(resp.data));
          let axiosInstance = axios.create();
          intercept(axiosInstance);
          return await axiosInstance.request(request);
        })
        .catch((e) => {
          if (
            window.location.pathname !== '/login' &&
            (!e.response || e.response.status === 401)
          )
            navigate('/login');
        });
    }
    return error.response;
  },
);

到目前为止,效果很好。