如何用Redux-Observable处理Refresh Token?AccessDenied后重发请求问题
问题描述
我正在开发基于React、Redux和Redux-Observable的客户端应用,希望在初始请求收到AccessDenied异常时,自动发起Refresh Token请求并重发原请求。
我折腾了半天毫无头绪,只找到一篇旧指南但没法正常工作(可能是我操作错了)。我不需要现成的解决方案,只想要方向指引。
现有代码
Epics 实现
export const getDataEpic: Epic<LogoutAction> = (action$, _, { api }) => action$.pipe( ofType(GET_DATA), exhaustMap(() => api.Request(getDataRequest).pipe( map((data) => { ...handle data }), )), ); export const refreshEpic: Epic<RefreshAction> = (action$, _, { api }) => action$.pipe( ofType(REFRESH), exhaustMap(() => api.Request<RefreshResponse>(refreshRequest).pipe( mergeMap(({ data }) => { const { success, user, accessToken } = data.auth.refresh; const actions: Action[] = [success ? refreshSuccess() : refreshFailure()]; if (!success) return from(actions); actions.push(setUser(user)); api.SetAccessToken(accessToken); return from(actions); }), )), );
全局Epic错误处理器
export const RootEpic: Epic = (action$, store$, dependencies: EpicDependencies) => combineEpics<any, any, any, any>( getDataEpic, refreshEpic, ... )(action$, store$, dependencies).pipe( catchError((error, source$) => { const api = dependencies.api; if (api.IsAccessDeniedError(error)) { return source$.pipe( ofType(REFRESH_SUCCESS), takeUntil(action$.pipe(ofType(REFRESH_FAILURE))), take(1), mergeMap(() => { return source$; }), mergeWith(of(refresh())), ); } console.error(error); return source$; }), );
当前问题表现
数据请求收到accessDenied时会发送refresh请求,但不会重新派发getData动作。我试过多种方法,甚至怀疑方案可行性,还试过请求前检查令牌过期、用Redux中间件的方式。
方向指引
- 将错误捕获从全局移到单个请求Epic内部:当前全局
catchError捕获错误后,原请求的上下文(比如GET_DATA动作)已经流失,刷新成功后无法触发重发。把错误处理放到每个需要令牌的请求Epic里,就能保留原动作的信息。 - 保留原请求动作用于重发:在单个请求Epic的
catchError中,捕获到AccessDenied时,要把当前触发请求的动作(比如GET_DATA)存下来,等REFRESH_SUCCESS动作触发后,重新派发这个原动作。 - 防止重复刷新请求:当多个请求同时触发
AccessDenied时,要确保同一时间只发起一次刷新。可以用exhaustMap包裹刷新逻辑,或者在Redux状态中维护isRefreshing状态,后续请求进入等待队列,刷新完成后批量重发。 - 利用状态管理刷新流程:在Redux状态中记录刷新状态(是否正在刷新、等待重发的请求队列),请求Epic先检查状态,若正在刷新则将原请求加入队列,刷新成功后遍历队列重发所有请求。
- 考虑请求库拦截器配合:如果使用Axios这类HTTP库,可以在请求/响应拦截器中处理令牌刷新,但要注意和Redux-Observable的状态同步,避免出现状态不一致的问题。
内容的提问来源于stack exchange,提问作者Davyd Melnychenko
相关产品推荐
相关产品推荐

