我已经构建了一个axios私有示例,它带有拦截器来管理auth请求。
系统有一个自定义axios示例:
const BASE_URL = 'http://localhost:8000';
export const axiosPrivate = axios.create({
baseURL: BASE_URL,
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
});
自定义useRefreshToken挂接使用刷新标记返回accessToken:
const useRefreshToken = () => {
const { setAuth } = useAuth();
const refresh = async () => {
const response = await refreshTokens();
// console.log('response', response);
const { user, roles, accessToken } = response.data;
setAuth({ user, roles, accessToken });
// return accessToken for use in axiosClient
return accessToken;
};
return refresh;
};
export default useRefreshToken;
Axios拦截器在useAxiosPrivate.js文件中附加到该axios示例,以附加accessToken,从而在accessToken过期时使用刷新令牌请求和刷新accessToken。
const useAxiosPrivate = () => {
const { auth } = useAuth();
const refresh = useRefreshToken();
useEffect(() => {
const requestIntercept = axiosPrivate.interceptors.request.use(
(config) => {
// attach the access token to the request if missing
if (!config.headers['Authorization']) {
config.headers['Authorization'] = `Bearer ${auth?.accessToken}`;
}
return config;
},
(error) => Promise.reject(error)
);
const responseIntercept = axiosPrivate.interceptors.response.use(
(response) => response,
async (error) => {
const prevRequest = error?.config;
// sent = custom property, after 1st request - sent = true, so no looping requests
if (error?.response?.status === 403 && !prevRequest?.sent) {
prevRequest.sent = true;
const newAccessToken = await refresh();
prevRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
return axiosPrivate(prevRequest);
}
return Promise.reject(error);
}
);
// remove the interceptor when the component unmounts
return () => {
axiosPrivate.interceptors.response.eject(responseIntercept);
axiosPrivate.interceptors.request.eject(requestIntercept);
};
}, [auth, refresh]);
return axiosPrivate;
};
export default useAxiosPrivate;
现在,这个私有axios示例在功能组件- PanelLayout中被调用,该组件用于 Package 页面并提供布局。
在这里,我尝试使用axios中的AbortControllers在组件挂载后终止请求。
function PanelLayout({ children, title }) {
const [user, setUser] = useState(null);
const axiosPrivate = useAxiosPrivate();
const router = useRouter();
useEffect(() => {
let isMounted = true;
const controller = new AbortController();
const signal = controller.signal;
const getUserProfile = async () => {
try {
const response = await axiosPrivate.get('/api/identity/profile', {
signal,
});
console.log(response.data);
isMounted && setUser(response.data.user);
} catch (error) {
console.log(error);
router.push({
pathname: '/seller/auth/login',
query: { from: router.pathname },
});
}
};
getUserProfile();
return () => {
isMounted = false;
controller.abort();
};
}, []);
console.log('page rendered');
return (
<div className='flex items-start'>
<Sidebar className='h-screen w-[10rem]' />
<section className='min-h-screen flex flex-col'>
<PanelHeader title={title} classname='left-[10rem] h-[3.5rem]' />
<main className='mt-[3.5rem] flex-1'>{children}</main>
</section>
</div>
);
}
export default PanelLayout;
但是,上述代码会引发以下错误:
CanceledError {message: 'canceled', name: 'CanceledError', code: 'ERR_CANCELED'}
code: "ERR_CANCELED"
message: "canceled"
name: "CanceledError"
[[Prototype]]: AxiosError
constructor: ƒ CanceledError(message)
__CANCEL__: true
[[Prototype]]: Error
请建议如何避免上述错误并使axios正常工作。
2条答案
按热度按时间x6492ojm1#
我也遇到了同样的问题,我认为我的逻辑中有一些缺陷,导致组件被安装了两次。在做了一些调查之后,我发现react显然在新版本18的StrictMode中添加了这个功能,其中useEffect被运行了两次。这里有一个链接,指向清楚解释这个新行为的文章。
解决此问题的一种方法是从应用程序中删除StrictMode(临时解决方案)
另一种方法是使用useRef钩子来存储一些状态,这些状态在应用程序第二次挂载时更新。
从this YouTube视频中找到了解决方案。
hmmo2u0o2#
我也遇到过这个问题。更糟糕的是,当请求被取消时,axios不提供HTTP状态代码,尽管你得到了
error.code === "ERR_CANCELED"
。我通过在axios拦截器中处理abort来解决这个问题:正如您所看到的,我确保了在异常中止的情况下,错误响应提供了
status
代码499
。