对于需要重复发生的代码以及需要无缝和通用的代码,中间件通常是要走的路。它可以像在创建商店时添加两行代码以包含中间件并编写一个将为您处理令牌逻辑的简单函数一样简单。
假设您将这样创建您的商店:
import { createStore, applyMiddleware, compose } from 'redux';
import rootReducer from './reducers';
import { browserHistory } from 'react-router';
import { routerMiddleware } from 'react-router-redux';
import tokenMiddleware from './middleware/token';
const finalCreateStore = compose(
applyMiddleware(
routerMiddleware(browserHistory),
tokenMiddleware,
),
window.devToolsExtension ? window.devToolsExtension() : f => f,
)(createStore);
然后你会从某个地方调用这个函数,并带有初始状态。
const store = finalCreateStore(rootReducer, initialState);
这将使您能够对通过商店的所有操作执行某些操作。由于使用 Promise 处理 API 调用的中间件很常见,因此有些人更愿意为此目的重用该中间件并将两者捆绑在一起。
典型的中间件如下所示:
export const tokenMiddleware = ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') { // pass along
return action(dispatch, getState);
}
// so let's say you have a token that's about to expire
// and you would like to refresh it, let's write so pseudo code
const currentState = getState();
const userObj = state.authentication.user;
if (userObj.token && userObj.token.aboutToExpire) {
const config = getSomeConfigs();
// some date calculation based on expiry time that we set in configs
const now = new Date();
const refreshThreshold = config.token.refreshThreshold;
if (aboutToExpireAndIsBelowThresholdToRefresh) {
// refreshTheToken can be an action creator
// from your auth module for example
// it should probably be a thunk so that you can handle
// an api call and a promise within once you get the new token
next(refreshTheToken(userObj, someOtherParams);
}
}
....
return next(action);
}
您的刷新令牌 thunk 可能类似于以下内容:
function refreshToken(user, maybeSomeOtherParams) {
const config = getSomeConfigs;
return dispatch => {
makeAPostCallWithParamsThatReturnsPromise
.then(result => dispatch(saveNewToken({
result,
...
})))
.catch(error => dispatch({
type: uh_oh_token_refresh_failed_action_type,
error,
}));
};
您可能会选择的另一种选择是在更改路线时处理此问题。
假设您将在某处为需要身份验证的路由和系统中存在的有效用户提供顶级路由。我们就叫他们authenticated routes吧。
您可以用顶级路由包装这些authenticated routes,该路由定义onChange 处理函数。像这样的:
<Route onChange={authEntry}>
<Route ... /> // authenticated routes
<Route ... />
</Route>
在创建这些路由并设置您的商店时,一旦您创建了商店,您就可以将其绑定到这个名为 checkAuth 的函数。
const authEntry = checkAuth.bind(null, store)
另一种方法是将路由定义包装在一个函数中并将存储传递给它,然后您将拥有相同的访问权限,但我发现它没有这个干净(个人喜好)。
现在checkAuth 会做什么?
类似这样的:
export function checkAuth (store, previous, next, replace, callback) {
const currentUser = store.getState().auth.user
// can possibly dispatch actions from here too
// store.dispatch(..).then(() => callback())..
// so you could possibly refresh the token here using an API call
// if it is about to expire
// you can also check if the token did actually expire and/or
// there's no logged in user trying to access the route, so you can redirect
if (!currentUser || !isLoggedIn(currentUser)) {
replace('/yourLoginRouteHere')
}
callback() // pass it along
}
这两者都应该足够通用,以便它们在一个集中的位置为您提供可重用的代码。希望这些对您有所帮助。