我正在编写一个 Redux 中间件,它负责在用户点击redux-api-middlewareAPI_CALL
之前将用户的 OAuth AccessToken 添加到阳离子。
// sign appends the `Authorization` HTTP Header to an API_CALL action
function sign(action) {
action[CALL_API].headers = {
...action[CALL_API].headers,
Authorization: `Bearer ${getState()auth.accessToken}`;
}
}
// Redux middleware signature.
return ({dispatch, getState}) => {
return next => action => {
if (action[CALL_API]) {
sign(action);
}
return next(action);
}
}
但是,我也希望这个中间件检测用户的 AccesToken 何时过期......
function tokenExpired() {
return (Date.now() > getState().auth.expirationTime);
}
发生这种情况时,中间件会保留该操作(防止将其传递给next
链中的中间件)并将其存储在内部列表中。然后它开始异步。通过分派“刷新访问令牌”FSA 进行令牌刷新过程:
if (tokenExpired()) {
// detainActions is an array, declared outside
// of the middleware's scope.
detainActions.push(action);
dispatch(refreshAccessToken());
}
else {
next(sign(action));
}
最后,我想监听“刷新访问令牌”流程何时完成并刷新(重新调度)所有被扣留的操作。AUTHENTICATION_RESPONSE
目前,我通过在FSA 流经我的中间件时寻找它们来做到这一点(该操作作为thunkAUTHENTICATION_RESPONSE
的副作用进行调度。refreshAccessToken
// Redux middleware signature.
return ({dispatch, getState}) => {
return next => action => {
if (action.type === AUTHENTICATION_RESPONSE && !action.error) {
// Let the AuthResponse action pass to the store.
next(action);
// Flush detained actions now we have a new grant.
return flushAndRedispatchDetainedActions(dispatch);
}
else {
// Sign CALL_API requests logic as above.
}
}
}
但是我对这种方法并不满意,因为不确定AUTHENTICATION_RESPONSE
FSA 是否真的会命中减速器(它可能被其他中间件拦截,或者被进一步推迟)。
我考虑过的可能替代方法是让refreshAccessToken
actionCreator 返回一个返回 Promise 的 thunk;这样我的中间件可以在刷新和重放所有请求之前等待该承诺解决,即:
if (tokenExpired()) {
// refreshAccessToken thunk returns a Promise.
dispatch(refreshAccessToken());
.then(flushAndRedispatchDetainedActions();
}
或者,我可以让我的中间件直接观察商店并在auth.accessToken
值发生变化时触发动作 - 但是,我不清楚中间件观察商店的指导是什么(我猜这是不可能的,因为中间件需要是在创建最终存储对象之前实例化。)
谢谢 :)
更新
在回家的路上思考问题;如果我要将实际的身份验证逻辑移出refreshAccessToken
(一个 thunk'd 动作创建者),并移到中间件本身,那么我会省去很多痛苦:
// Redux middleware signature.
return ({dispatch, getState}) => {
return next => action => {
if (action[CALL_AUTH]) {
authHelper.refreshGrant()
.then(grant => dispatch(processGrant(newGrant));
}
else {
// Sign CALL_API requests logic as above.
}
}
}