如何通过自定义中间件在RTK Query发起前获取Token?
Redux中间件异步获取Token未等待完成导致RTK Query请求无Token问题
我要开发一个Redux中间件,在RTK Query发起动作时获取Token(后续会添加Token过期检查,当前仅做获取测试)。但从Azure获取Token是异步操作,中间件没有等待Token获取完成就继续执行,导致RTK Query发起数据请求时Token尚未准备就绪。我对Redux及中间件中的异步逻辑理解可能存在偏差。
authMiddleware.ts
export const authMiddleware: Middleware = ({ dispatch, getState }) => next => async (action: PayloadAction) => { // const store = getState() as RootState; console.log('Dispatching action:', action.type); logWithTime('Middleware: action is a function and started'); if (action.type === 'apiInvoices/config/middlewareRegistered') { logWithTime( 'middleware in action: apiInvoices/config/middlewareRegistered' ); let token: string | void = ''; console.log('Action in if: ', action.type) const tokenProvider: AadTokenProvider = await getAadTokenProvider(); if (tokenProvider) { logWithTime('Token provider set'); } // check if tokenProvider is not null if (tokenProvider) { console.log('Token provider:', tokenProvider); } else { console.log('Token provider is null') } // fetch token, wait till token is fetched and then set // token in store then return next action token = await tokenProvider.getToken( '6cbc9b1e-901d-4a99-a947-ae36ffe3ac38' ) .then(token => { setToken({ endpoint: 'getInvoiceByID', token: token }) }) .then(() => { logWithTime('Token fetched and set in store'); next(action) }); } };
store.ts
import { configureStore } from '@reduxjs/toolkit'; import { tokenReducer } from '../slices/tokenSlice'; import { apiInvoices } from '../api/invoiceApi'; import logger from 'redux-logger'; import { authMiddleware } from '../middleware/authMiddleware/authMiddleware'; const store = configureStore({ reducer: { token: tokenReducer, [apiInvoices.reducerPath]: apiInvoices.reducer, // other reducers }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({}) .concat(apiInvoices.middleware, authMiddleware, logger), }); export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch; export default store
控制台输出(Token获取过晚)
Dispatching action: apiInvoices/config/middlewareRegistered authMiddleware.ts:15 [2024-11-14T08:06:58.937Z] Middleware: action is a function and started authMiddleware.ts:15 [2024-11-14T08:06:58.937Z] middleware in action: apiInvoices/config/middlewareRegistered authMiddleware.ts:25 Action in if: apiInvoices/config/middlewareRegistered authMiddleware.ts:15 [2024-11-14T08:06:58.940Z] Token provider set authMiddleware.ts:33 Token provider: e {_aadConfiguration: {…}, _oboConfiguration: {…}, _tokenAcquisitionEvent: e, onBeforeRedirectEvent: e, popupEvent: e, …} authMiddleware.ts:19 Dispatching action: apiInvoices/executeQuery/pending authMiddleware.ts:15 [2024-11-14T08:06:58.944Z] Middleware: action is a function and started authMiddleware.ts:15 [2024-11-14T08:06:58.944Z] Middleware: action is a function and ended authMiddleware.ts:42 action apiInvoices/executeQuery/pending @ 09:06:58.944
问题原因与解决方法
核心问题
- 你监听的
apiInvoices/config/middlewareRegistered是RTK Query初始化动作,该动作执行完毕后RTK Query会立即发起请求,但你的中间件仅在初始化动作里处理Token,未拦截后续的请求动作。 - 混合使用
await和.then()导致逻辑混乱,异步流程未正确阻塞请求动作的执行。
修正方案
1. 调整中间件拦截目标
改为监听RTK Query的请求发起动作,在请求前确保Token已就绪:
export const authMiddleware: Middleware = ({ dispatch, getState }) => next => async (action: PayloadAction) => { // 拦截RTK Query的请求动作 if (action.type.startsWith('apiInvoices/executeQuery')) { const state = getState() as RootState; // 先从store取已有的Token(后续可添加过期判断) let token = state.token.getInvoiceByID; if (!token) { // 无有效Token则异步获取 const tokenProvider = await getAadTokenProvider(); if (tokenProvider) { token = await tokenProvider.getToken('6cbc9b1e-901d-4a99-a947-ae36ffe3ac38'); // 将Token存入store dispatch(setToken({ endpoint: 'getInvoiceByID', token })); } } // Token存在才放行请求 if (token) { return next(action); } else { console.error('获取认证Token失败'); return; } } // 非目标动作直接放行 return next(action); };
2. 调整中间件顺序
在store.ts中将authMiddleware放在apiInvoices.middleware之前,确保请求先经过Token处理:
middleware: (getDefaultMiddleware) => getDefaultMiddleware({}) .concat(authMiddleware, apiInvoices.middleware, logger),
3. 统一异步写法
避免await和.then()混用,全部使用await让逻辑更清晰易维护。
内容的提问来源于stack exchange,提问作者Jakub Husařík
相关产品推荐
相关产品推荐

