axios 请求只报泛化 Network Error:如何为网络错误分类并输出详细原因
axios 默认对大量不同的网络故障抛出同一个 "Network Error" 消息:断网、DNS 解析失败、服务器拒绝连接、CORS 拦截、请求超时,在 catch 里看起来几乎一样,调试时无法区分根因。本场景的目标是:在 axios 上挂一个响应拦截器,对错误做分类,给每个错误对象重写 error.code(如 ERR_TIMEOUT、ERR_DNS_FAILURE)并新增一个 error.detailedMessage 字段输出可读原因。仓库中的 examples/improved-network-errors.md 与 examples/network_enhanced.js 提供了完整的方案与实现,按浏览器和 Node.js 两种环境均可使用。
前提只需安装 axios:
npm install axios
先弄清:为什么 axios 只报泛化错误
查阅 官方错误处理文档 的错误码表,与网络故障相关的有两个关键点:
ERR_NETWORK:网络类问题。在浏览器中,这个错误还可能由 CORS 或 Mixed Content 策略违规引起。文档明确指出:浏览器不允许 JS 代码澄清由安全问题引起的错误真实原因,需要自行查看控制台。- 超时默认报
ECONNABORTED;只有设置transitional.clarifyTimeoutError: true后才会报ETIMEDOUT,这样超时错误才能和其他中断区分开。
也就是说,axios 本身的错误对象信息有限(浏览器安全限制决定了这一点),要输出详细原因,需要在业务层基于 error.code、error.message 和响应状态码做二次分类。
方案:响应拦截器 + 错误增强函数
examples/improved-network-errors.md 描述的机制:
- 用
axios.create(config)创建实例; - 通过
client.interceptors.response.use()注册响应拦截器,在拒绝分支里捕获错误(拦截器机制见 Interceptors 文档); - 拦截器调用
enhanceNetworkError(error):根据error.code、error.message、error.response.status判断错误类型,改写error.code,挂上新字段error.detailedMessage,然后重新throw出去。
完整实现见仓库中的 examples/network_enhanced.js,核心代码如下:
import axios from 'axios';
function enhanceNetworkError(error) {
// when Offline (no internet)
if (typeof navigator !== 'undefined' && !navigator.onLine) {
error.code = 'ERR_NO_INTERNET';
error.detailedMessage =
'No internet connection detected. Please check your connection and try again.';
}
// when DNS failure occurs (invalid domain)
else if (error.code === 'ENOTFOUND' || /dns/i.test(error.message)) {
error.code = 'ERR_DNS_FAILURE';
error.detailedMessage =
'Unable to reach the requested domain. Please verify the URL or your network settings.';
}
// when Connection refused by server
else if (error.code === 'ECONNREFUSED' || /refused/i.test(error.message)) {
error.code = 'ERR_CONNECTION_REFUSED';
error.detailedMessage =
'Connection was refused by the server. It may be temporarily unavailable.';
}
// when Request timeout happens
else if (error.code === 'ETIMEDOUT' || /timeout/i.test(error.message)) {
error.code = 'ERR_TIMEOUT';
error.detailedMessage = 'The request took too long to respond. Please try again later.';
}
// when CORS restriction happens (for browser only)
else if (/CORS/i.test(error.message)) {
error.code = 'ERR_CORS_BLOCKED';
error.detailedMessage = 'The request was blocked due to cross-origin restrictions.';
}
// when Server-side error occurs
else if (error.response && error.response.status >= 500) {
error.code = 'ERR_SERVER';
error.detailedMessage = 'A server-side issue occurred. Please try again later.';
}
// when Client-side error occurs
else if (error.response && error.response.status >= 400) {
error.code = 'ERR_CLIENT';
error.detailedMessage = 'A client-side error occurred. Please check your request.';
}
// when unknown network issue occurs
else {
error.code = 'ERR_NETWORK_GENERIC';
error.detailedMessage =
'A network issue occurred. Please check your connection or try again later.';
}
return error;
}
export function createEnhancedClient(config = {}) {
const client = axios.create(config);
client.interceptors.response.use(
(response) => response,
(error) => {
throw enhanceNetworkError(error);
}
);
return client;
}
export default enhanceNetworkError;
分类规则按 if/else 链的先后顺序匹配(先命中先归类):
| 判断条件 | 归类 code | detailedMessage 含义 |
|---|---|---|
navigator.onLine 为 false(浏览器) |
ERR_NO_INTERNET |
无网络连接 |
error.code === 'ENOTFOUND' 或 message 含 dns |
ERR_DNS_FAILURE |
域名无法解析 |
error.code === 'ECONNREFUSED' 或 message 含 refused |
ERR_CONNECTION_REFUSED |
服务器拒绝连接 |
error.code === 'ETIMEDOUT' 或 message 含 timeout |
ERR_TIMEOUT |
请求超时 |
message 含 CORS(浏览器) |
ERR_CORS_BLOCKED |
跨域被拦截 |
error.response.status >= 500 |
ERR_SERVER |
服务端错误 |
error.response.status >= 400 |
ERR_CLIENT |
客户端错误 |
| 以上都不满足 | ERR_NETWORK_GENERIC |
未识别的网络问题 |
注意 5xx 的判断在 4xx 之前,因此 500 以上归类为 ERR_SERVER,400–499 归类为 ERR_CLIENT。
使用与验证
用 createEnhancedClient() 创建客户端即可,之后所有请求的错误都会带上分类信息。以下示例中的 baseURL 是文档示例值,替换为你自己的接口地址:
const api = createEnhancedClient({ baseURL: 'https://example.com' });
api
.get('/data')
.then((res) => console.log(res.data))
.catch((err) => {
console.error(err.code); // e.g., ERR_TIMEOUT
console.error(err.detailedMessage); // e.g., "The request took too long to respond."
});
验证方式:发起一个必然失败的请求(超时、错误域名、断网均可),在 catch 中打印 err.code 和 err.detailedMessage,能按上表输出对应分类即说明拦截器生效。上面代码块中的两个 // e.g., 值是文档给出的示例输出,不是固定预期。
若想在分类前先检查 axios 原始错误,错误处理文档 给出了三分支判断:error.response 存在表示服务器已响应但状态码不在 2xx 范围;error.request 存在表示请求已发出但没有收到响应(浏览器中是 XMLHttpRequest 实例,Node.js 中是 http.ClientRequest 实例);两者都没有则是请求设置阶段出错。错误对象还支持 error.toJSON() 获取完整信息,含 message、name、stack、config、code、status 字段。
让超时错误可靠命中超时分支
enhanceNetworkError 的超时分支依赖 ETIMEDOUT(或 message 含 timeout)。而 axios 默认超时报的是 ECONNABORTED。按 官方超时处理文档,应在请求配置(或 createEnhancedClient 传入的 config)中设置 timeout 并开启 clarifyTimeoutError:
const response = await axios.get("https://example.com/data", {
timeout: 5000, // 5 seconds
transitional: {
// set to true if you prefer ETIMEDOUT over ECONNABORTED
clarifyTimeoutError: true,
},
});
官方文档同时提示:生产环境应始终设置 timeout,否则卡住的请求可能无限挂起。clarifyTimeoutError 的含义与默认值(false)见 request-config 文档。
限制与边界
- 浏览器中的 CORS/Mixed Content 无法可靠识别:官方文档明确说明浏览器不允许 JS 代码澄清这类安全错误的真实原因,要定位需查看控制台。
/CORS/i.test(error.message)这条规则只在错误 message 中确实含有CORS字样时才命中,不能保证覆盖所有跨域失败。 - 离线检测只在浏览器生效:
ERR_NO_INTERNET分支依赖navigator.onLine,Node.js 环境中该分支不成立,断网会落到后续规则或ERR_NETWORK_GENERIC。 - 分类顺序固定:
if/else链保证先命中的规则优先(如离线判断在 DNS 判断之前),不要随意调整顺序。 - 该方案是业务层封装:它改写的是你应用持有的错误对象,axios 抛出的原始错误(
ERR_NETWORK、ECONNABORTED等)行为不变,其他使用 axios 的模块不受此拦截器影响。
参考文件
- examples/improved-network-errors.md:问题描述、分类映射与用法示例
- examples/network_enhanced.js:
enhanceNetworkError与createEnhancedClient完整实现 - docs/pages/advanced/error-handling.md:axios 官方错误结构、错误码表与超时处理
- docs/pages/advanced/interceptors.md:拦截器机制与执行顺序
- docs/pages/advanced/request-config.md:
timeout、transitional.clarifyTimeoutError配置说明
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00