这是我的问题。 我在项目中使用JWT身份验证,我的React项目中还设置了axiosInstance。 我还为axiosInstance设置了拦截器,负责在需要时拦截并刷新令牌。
const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 360000,
transformRequest: [
function (data, headers) {
const accessToken = window.localStorage.getItem('access_token');
if (accessToken) {
headers['Authorization'] = `Bearer ${accessToken}`;
} else {
delete headers.Authorization;
}
return JSON.stringify(data);
},
],
headers: {
'Content-Type': 'application/json',
accept: 'application/json',
},
});
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async function (error) {
const originalRequest = error.config;
console.log(
'Caught the error response. Here is your request ',
originalRequest,
);
// case 1: No error specified Most likely to be server error
if (typeof error.response === 'undefined') {
// Uncomment this later
alert('Server error occured');
return Promise.reject(error);
}
// case 2: Tried to refresh the token but it is expired. So ask user to login again
if (
error.response.status === 401 &&
originalRequest.url === baseURL + 'auth/api/token/refresh/'
) {
store.dispatch(setLoginFalse());
return Promise.reject(error);
}
// Case 3: Got 401 Unauthorized error. There are different possiblities
console.log('Error message in axios = ', error.response.data);
if (
error.response.status === 401 &&
error.response.statusText === 'Unauthorized'
) {
const refreshToken = localStorage.getItem('refresh_token');
console.log('Refresh token = ', refreshToken);
// See if refresh token exists
// Some times undefined gets written in place of refresh token.
// To avoid that we check if refreshToken !== "undefined". This bug is still unknown need to do more research on this
if (refreshToken !== undefined && refreshToken !== 'undefined') {
console.log(typeof refreshToken == 'undefined');
console.log('Refresh token is present = ', refreshToken);
const tokenParts = JSON.parse(atob(refreshToken.split('.')[1]));
// exp date in token is expressed in seconds, while now() returns milliseconds:
const now = Math.ceil(Date.now() / 1000);
console.log(tokenParts.exp);
// Case 3.a Refresh token is present and it is not expired - use it to get new access token
if (tokenParts.exp > now) {
return axiosInstance
.post('auth/api/token/refresh/', { refresh: refreshToken })
.then((response) => {
localStorage.setItem('access_token', response.data.access);
axiosInstance.defaults.headers['Authorization'] =
'Bearer ' + response.data.access;
originalRequest.headers['Authorization'] =
'Bearer ' + response.data.access;
console.log('access token updated');
// After refreshing the token request again user's previous url
// which was blocked due to unauthorized error
// I am not sure by default axios performs get request
// But since we are passing the entire config of previous request
// It seems to perform same request method as previous
return axiosInstance(originalRequest);
})
.catch((err) => {
// If any error occurs at this point we cannot guess what it is
// So just console log it
console.log(err);
});
} else {
// Refresh token is expired ask user to login again.
console.log('Refresh token is expired', tokenParts.exp, now);
store.dispatch(setLoginFalse());
}
} else {
// refresh token is not present in local storage so ask user to login again
console.log('Refresh token not available.');
store.dispatch(setLoginFalse());
}
}
// specific error handling done elsewhere
return Promise.reject(error);
},
);
export default axiosInstance;
请注意,我在 axiosInstance 中设置了 Content-Type 为 'application/json'。
但是我的问题是,为了上传图像,内容类型应该为“multipart/form-data--boundary: set-automatically”。
(注意:手动设置多部分数据的边界似乎不起作用)
如果我们没有在标头中放置内容类型,axios 会自动设置多部分数据的边界。但是我必须找到一种方法,在不影响项目其他部分使用的 axiosInstance 的情况下,在一个地方从 axiosInstance 中删除 content-type(从我上传图片的地方)。
我使用 fetch 进行了测试,并设置了新的 axios 实例,结果达到了预期。但问题是这些请求将不会被 axios 拦截以刷新 JWT 令牌(如果需要)。
我阅读了各种帖子,但仍然看不到解决此问题的方法。
如果需要,我可以提供更多细节。请帮帮我,我已经花费了 8+ 小时来调试它。
谢谢。
编辑 1
我将 handleSubmit 函数更改为以下内容
const handleSubmit = (e) => {
e.preventDefault();
console.log(file);
let formData = new FormData();
formData.append('profile_pic', file);
formData.append('name', 'root');
axiosInstance.defaults.headers.common['Content-Type'] =
'multipart/form-data';
axiosInstance
.put('/users/profile-pic-upload/', formData)
.then((res) => console.log(res))
.catch((err) => console.log(err));
};
但是假设我在axios.js中更改了content-type为'multipart/form-data',它会更改所有请求的内容类型。这会破坏其他事情,但是如预期的那样,它不会解决此问题。因为设置手动边界似乎无效。甚至这篇文章也建议在多部分数据时删除内容类型,以便库(在这种情况下为axios)自动处理。
contentType
进行 记忆化 调用。虽然不是必须的,但假设你有 lodash 的memoize
可用:const customAxios = _.memoize((contentType) => { ... });
- romellem