我正在编写一段Redux中间件,负责将用户的OAuth AccessToken添加到API_CALL
个阳台,然后再点击redux-api-middleware。
// 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在我的中间件流动时这样做(AUTHENTICATION_RESPONSE
操作被调度为refreshAccessToken
thunk的副作用。
// 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.
}
}
}