我有一个react/redux应用程序,从api服务器获取一个令牌。在用户进行身份验证之后,我希望所有axios请求都具有该令牌作为授权标头,而不必手动将其附加到操作中的每个请求。我是相当新的反应/redux,我不确定最好的方法,我没有找到任何高质量的谷歌。
这是我的redux设置:
// actions.js
import axios from 'axios';
export function loginUser(props) {
const url = `https://api.mydomain.com/login/`;
const { email, password } = props;
const request = axios.post(url, { email, password });
return {
type: LOGIN_USER,
payload: request
};
}
export function fetchPages() {
/* here is where I'd like the header to be attached automatically if the user
has logged in */
const request = axios.get(PAGES_URL);
return {
type: FETCH_PAGES,
payload: request
};
}
// reducers.js
const initialState = {
isAuthenticated: false,
token: null
};
export default (state = initialState, action) => {
switch(action.type) {
case LOGIN_USER:
// here is where I believe I should be attaching the header to all axios requests.
return {
token: action.payload.data.key,
isAuthenticated: true
};
case LOGOUT_USER:
// i would remove the header from all axios requests here.
return initialState;
default:
return state;
}
}
我的令牌存储在redux store下的state.session.token。
我有点不知道该怎么做。我尝试在根目录的文件中创建一个axios实例,并更新/导入该实例,而不是从node_modules中更新/导入,但当状态发生变化时,它没有附加头文件。任何反馈/想法都非常感谢,谢谢。
export const authHandler = (config) => {
const authRegex = /^\/apiregex/;
if (!authRegex.test(config.url)) {
return store.fetchToken().then((token) => {
Object.assign(config.headers.common, { Authorization: `Bearer ${token}` });
return Promise.resolve(config);
});
}
return Promise.resolve(config);
};
axios.interceptors.request.use(authHandler);
在尝试实现类似的东西时遇到了一些陷阱,基于这些答案,这是我想出的。我遇到的问题是:
If using axios for the request to get a token in your store, you need to detect the path before adding the header. If you don't, it will try to add the header to that call as well and get into a circular path issue. The inverse of adding regex to detect the other calls would also work
If the store is returning a promise, you need to return the call to the store to resolve the promise in the authHandler function. Async/Await functionality would make this easier/more obvious
If the call for the auth token fails or is the call to get the token, you still want to resolve a promise with the config
如果你想在未来调用其他api路由,并将你的令牌保存在存储中,那么尝试使用redux中间件。
中间件可以监听api动作,并通过axios相应地分派api请求。
这里有一个非常基本的例子:
行动/ api.js
export const CALL_API = 'CALL_API';
function onSuccess(payload) {
return {
type: 'SUCCESS',
payload
};
}
function onError(payload) {
return {
type: 'ERROR',
payload,
error: true
};
}
export function apiLogin(credentials) {
return {
onSuccess,
onError,
type: CALL_API,
params: { ...credentials },
method: 'post',
url: 'login'
};
}
中间件/ api.js
import axios from 'axios';
import { CALL_API } from '../actions/api';
export default ({ getState, dispatch }) => next => async action => {
// Ignore anything that's not calling the api
if (action.type !== CALL_API) {
return next(action);
}
// Grab the token from state
const { token } = getState().session;
// Format the request and attach the token.
const { method, onSuccess, onError, params, url } = action;
const defaultOptions = {
headers: {
Authorization: token ? `Token ${token}` : '',
}
};
const options = {
...defaultOptions,
...params
};
try {
const response = await axios[method](url, options);
dispatch(onSuccess(response.data));
} catch (error) {
dispatch(onError(error.data));
}
return next(action);
};
创建axios实例:
// Default config options
const defaultOptions = {
baseURL: <CHANGE-TO-URL>,
headers: {
'Content-Type': 'application/json',
},
};
// Create instance
let instance = axios.create(defaultOptions);
// Set the AUTH token for any request
instance.interceptors.request.use(function (config) {
const token = localStorage.getItem('token');
config.headers.Authorization = token ? `Bearer ${token}` : '';
return config;
});
然后,对于任何请求,将从localStorage中选择令牌,并将其添加到请求头中。
我在整个应用程序中使用相同的实例:
import axios from 'axios';
const fetchClient = () => {
const defaultOptions = {
baseURL: process.env.REACT_APP_API_PATH,
method: 'get',
headers: {
'Content-Type': 'application/json',
},
};
// Create instance
let instance = axios.create(defaultOptions);
// Set the AUTH token for any request
instance.interceptors.request.use(function (config) {
const token = localStorage.getItem('token');
config.headers.Authorization = token ? `Bearer ${token}` : '';
return config;
});
return instance;
};
export default fetchClient();