TS封装Axios 401刷新token后重试报错无法读取undefined的cancelToken属性
问题核心原因
- 参数传递错误:响应拦截器的错误回调中,你只提取了
error.response传入handleError,但重试需要用到的原始请求配置config是挂载在顶层error对象上的,不在response里。你把response传入handleError后还将参数命名为error,直接导致后续取error.config拿到的是undefined,调用this.request(undefined)自然会报无法读取undefined的cancelToken属性。 - 请求拦截器漏返回配置:
injectToken函数只在满足if条件时返回了config,不满足条件时没有任何返回值,axios会把请求配置设为undefined,后续所有依赖配置的操作都会报错。 - 错误边界缺失:
handleError直接解构参数的status属性,没有判断参数是否为undefined(比如网络错误时error.response本身就是undefined),直接触发无法读取undefined的status属性报错。 - 请求拦截器错误回调漏返回:请求拦截器的错误回调只写了
Promise.reject(error)没有加return,错误会被静默吞掉,变成成功状态的Promise,引发后续逻辑异常。
修复步骤
- 修改响应拦截器错误回调,把完整的
error对象传入handleError,不要只传response - 补全
injectToken函数的返回逻辑,所有分支都要返回config - 给
handleError加空值判断,避免无response时的报错 - 补全请求拦截器错误回调的return语句
- 可选优化:给请求加重试标记,避免refreshToken失效时进入无限401重试循环
核心代码修正示例
1. 修正injectToken函数
const injectToken = (config: AxiosRequestConfig): AxiosRequestConfig => { if ( config.baseURL === API_HOST && !config.headers.Authorization && useAuthStatus.getState().status === "logged-in" ) { try { const token = getAccessToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } } catch (error) { throw new Error(error); } } // 所有分支都要返回config return config; };
2. 修正请求拦截器错误回调
http.interceptors.request.use(injectToken, (error) => { // 补全return语句抛出错误 return Promise.reject(error); });
3. 修正响应拦截器错误回调
http.interceptors.response.use( (response) => { const { data } = response; return data; }, (error) => { // 传入完整error对象,不要只传response return this.handleError(error); } );
4. 修正handleError逻辑
private handleError(error: AxiosError) { // 先处理无响应的情况(网络错误、跨域等) if (!error.response) { return Promise.reject(error); } const { status } = error.response; // 从原始error对象上取请求配置 const originalRequestConfig = error.config as AxiosRequestConfig & { isRetry?: boolean }; switch (status) { case StatusCode.InternalServerError: { break; } case StatusCode.Forbidden: { // Handle Forbidden break; } case StatusCode.Unauthorized: { // 加重试标记避免死循环 if (useAuthStatus.getState().status === "logged-in" && !originalRequestConfig.isRetry) { originalRequestConfig.isRetry = true; return refreshToken().then((_) => { originalRequestConfig.headers.Authorization = `Bearer ${useTokenStore.getState().accessToken}`; return this.request(originalRequestConfig); }); } // 未登录或重试失败直接退出登录 useAuthStatus.getState().logout(); return Promise.reject(error); } case StatusCode.TooManyRequests: { // Handle TooManyRequests break; } } return Promise.reject(error); }
内容的提问来源于stack exchange,提问作者Fabio
相关产品推荐
相关产品推荐

