Axios 错误处理深度解析:AxiosError 结构、错误码、超时区分与敏感信息脱敏
【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios
axios 默认在请求失败时让 Promise 被 reject,而失败的具体形态由AxiosError对象承载。本文基于官方文档docs/fr/pages/advanced/error-handling.md并结合 axios 源码,完整解析 axios 抛出的错误结构(message、code、status、config、request、response等字段)、axios 内部识别的全部错误码及其在源码中的产生位置,以及validateStatus自定义判定、timeout超时错误区分(ECONNABORTED与ETIMEDOUT)、畸形 HTTP(S) URL 的严格校验,还有通过toJSON()序列化错误和使用redact配置避免密钥泄漏的实战方案。读完本文,你可以在浏览器与 Node.js 环境下编写结构清晰、可区分超时/网络/取消等错误类型、且不会在日志中泄露凭据的错误处理代码。
AxiosError:axios 错误的统一载体
axios 抛出的绝大多数错误都是AxiosError实例(由原生Error派生),文档给出的通用结构如下:
| 属性 | 定义 |
|---|---|
message | 错误消息与失败状态码的简短摘要。 |
name | 标识错误来源。来自 axios 的错误,该值始终为AxiosError。 |
stack | 提供错误的调用堆栈。 |
config | 请求发起时 axios 的配置对象,包含用户为请求/实例设置的各项配置。 |
code | 代表被 axios 识别的错误类型,具体取值见下一节的错误码表。 |
status | HTTP 响应状态码。 |
此外,AxiosError实例还带有一个布尔标记isAxiosError = true,这正是axios.isAxiosError(error)的判定依据。从源码看,isAxiosError.js 中该函数只做一次属性检查:
export default function isAxiosError(payload) { return utils.isObject(payload) && payload.isAxiosError === true; }而在 AxiosError.js 的构造函数中,各字段被显式挂到实例上(第 160–168 行):
this.name = 'AxiosError'; this.isAxiosError = true; code && (this.code = code); config && (this.config = config); request && (this.request = request); if (response) { this.response = response; this.status = response.status; }可以据此推断出文档中三个错误分支与源码字段的对应关系:只有拿到服务端响应(response)时才设置status;只有请求已发出但无响应时request才有值;而配置阶段的错误通常既没有request也没有response,只留下config与message。
axios 内部识别的错误码(code)
文档列出了 axios 可识别的全部错误码。这些错误码以静态属性形式定义在 AxiosError.js 中,可统一通过AxiosError.ERR_NETWORK等引用:
| 错误码 | 含义 |
|---|---|
ERR_BAD_OPTION_VALUE | axios 配置中提供了无效或不受支持的值。 |
ERR_BAD_OPTION | axios 配置中提供了无效的选项。 |
ECONNABORTED | 通常表示请求超时(除非设置了transitional.clarifyTimeoutError),或被浏览器/插件中止。 |
ETIMEDOUT | 请求超过了 axios 默认超时时间。需将transitional.clarifyTimeoutError设为true,否则抛出的仍是通用的ECONNABORTED。 |
ERR_NETWORK | 网络类问题。在浏览器中,CORS 违规或混合内容(mixed content)也可能导致此错误;出于安全考虑,浏览器不允许 JS 得知真实原因,请查看控制台。 |
ERR_FR_TOO_MANY_REDIRECTS | 请求被重定向的次数超过了 axios 配置中指定的最大值。 |
ERR_DEPRECATED | 使用了 axios 中已被弃用的功能或方法。 |
ERR_BAD_RESPONSE | 响应无法被正确解析或格式不符合预期,通常对应5xx状态码。 |
ERR_BAD_REQUEST | 请求格式不符合预期或缺少必需参数,通常对应4xx状态码。 |
ERR_CANCELED | 功能或方法被用户通过 AbortSignal(或 CancelToken)显式取消。 |
ERR_NOT_SUPPORT | 当前 axios 运行环境不支持该功能或方法。 |
ERR_INVALID_URL | axios 请求提供了无效的 URL。 |
ERR_FORM_DATA_DEPTH_EXCEEDED | 序列化params或表单数据时,某对象深度超过了配置的maxDepth,默认限制 100 层。可参考 请求配置文档 中的paramsSerializer与formSerializer说明。 |
源码中可以找到几个代表性错误码的产生点:
ERR_BAD_REQUEST/ERR_BAD_RESPONSE由 settle.js 在响应判定失败时抛出,状态码 4xx/5xx 与错误码的对应关系即在此处确定:
reject(new AxiosError( 'Request failed with status code ' + response.status, response.status >= 400 && response.status < 500 ? AxiosError.ERR_BAD_REQUEST : AxiosError.ERR_BAD_RESPONSE, response.config, response.request, response ));ERR_INVALID_URL由 buildFullPath.js 中的assertValidHttpProtocolURL抛出(详见下文"畸形 URL"一节),此外 fromDataURI.js 在解析非法 data URI 时也会使用该错误码。ERR_FORM_DATA_DEPTH_EXCEEDED分别由 toFormData.js 与 formDataToJSON.js 在递归序列化超过maxDepth时抛出。
捕获与处理错误:response / request / config 三分支
axios 的默认行为是:请求失败时 reject Promise。捕获错误后,推荐的判断顺序是"先查error.response,再查error.request,最后处理配置阶段的错误"。这是文档给出的标准示例:
axios.get("/user/12345").catch(function (error) { if (error.response) { // 请求已发出,且服务端返回了非 2xx 的状态码 console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { // 请求已发出,但未收到任何响应 // 浏览器中 `error.request` 是 XMLHttpRequest 实例, // Node.js 中是 http.ClientRequest 实例 console.log(error.request); } else { // 请求配置阶段就发生了错误 console.log("Error", error.message); } console.log(error.config); });这个三分支结构之所以可靠,是因为它直接对应AxiosError构造函数的赋值逻辑:response分支意味着响应已存在(服务端返回了非 2xx);request分支意味着请求已挂到实例上但响应缺失(网络中断、超时、CORS 等);否则就是配置校验、URL 解析等问题,此时只有config和message可用。在 Node.js 环境中,http.js 适配器抛错时会把原生http.ClientRequest作为request传入;浏览器中则由 xhr.js 传入XMLHttpRequest实例。
使用 validateStatus 自定义"何为失败"
axios 的默认判定是status >= 200 && status < 300时 resolve,否则 reject。通过配置项validateStatus可以覆盖这一条件,自行决定哪些 HTTP 状态码应当触发错误:
axios.get("/user/12345", { validateStatus: function (status) { return status < 500; // 只有状态码小于 500 才视为成功 }, });默认实现在 defaults/index.js 中定义,判定逻辑则在 settle.js 执行:若validateStatus(response.status)返回真值则 resolve,否则构造AxiosError并 reject。此外 mergeConfig.js 中还有一个transitional.validateStatusUndefinedResolves细节:当请求级配置显式传入validateStatus: undefined且该过渡开关为false时,会回退到实例级validateStatus,这允许你用请求级配置覆盖实例默认值。
处理超时:ECONNABORTED 与 ETIMEDOUT 的区分
当请求超过配置的timeout时,axios 默认以ECONNABORTED拒绝 Promise。若希望获得更精确的ETIMEDOUT错误码,需要设置transitional.clarifyTimeoutError: true:
async function fetchWithTimeout() { try { const response = await axios.get("https://example.com/data", { timeout: 5000, // 5 秒 transitional: { // 若希望用 ETIMEDOUT 替代 ECONNABORTED,设为 true clarifyTimeoutError: true, }, }); console.log("Response:", response.data); } catch (error) { if (axios.isAxiosError(error)) { if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") { console.error("Request timed out. Please try again."); return; } console.error("Axios error:", error.message); return; } console.error("Unexpected error:", error); } }源码层面,transitional.js 显示clarifyTimeoutError的默认值是false,因此不显式开启时超时一律是ECONNABORTED。浏览器适配器的request.ontimeout处理(xhr.js)体现了这一切换逻辑:
reject( new AxiosError( timeoutErrorMessage, transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED, config, request ) );Node.js 适配器 http.js 中的超时处理采用完全相同的三元表达式;另外 composeSignals.js 在组合 AbortSignal 超时时直接抛出ETIMEDOUT,可据此推断不同触发路径下错误码可能略有差异,编写兜底逻辑时建议同时兼容ECONNABORTED与ETIMEDOUT。
生产环境务必设置
timeout,否则被阻塞的请求可能永远处于挂起状态。相关配置项参见 请求配置文档 中的timeout与transitional.clarifyTimeoutError说明。
畸形 HTTP(S) URL:ERR_INVALID_URL 的严格校验
axios 会拒绝url或baseURL中协议后缺少//的http:/https:URL。例如https:example.com与https:/example.com不会被浏览器或 Node.js 的 URL 解析器"静默修正",而是直接抛出code为ERR_INVALID_URL的AxiosError。请使用https://example.com这类格式正确的 URL。
错误消息会明确指出有问题的 URL,例如:
Invalid URL "https:example.com": missing "//" after protocol这一行为在 buildFullPath.js 中实现,核心是一个正则与断言函数:
const malformedHttpProtocol = /^https?:(?!\/\/)/i; function assertValidHttpProtocolURL(url, config) { if (typeof url === 'string') { const normalizedURL = normalizeURLForProtocolCheck(url); if (malformedHttpProtocol.test(normalizedURL)) { throw new AxiosError( `Invalid URL ${JSON.stringify(redactSensitiveURLParts(normalizedURL))}: missing "//" after protocol`, AxiosError.ERR_INVALID_URL, config ); } } }其中 normalizeURLForProtocolCheck.js 会先对齐 WHATWG URL 的预处理规则:剔除前导空白字符并移除\t、\n、\r控制字符后再做协议检查,防止通过控制字符绕过校验。
安全动机:这种严格校验能阻止畸形 URL 绕过baseURL拼接逻辑或 URL 白名单机制。更重要的是,错误消息中对 URL 的展示做了系统性脱敏(redactSensitiveURLParts):
- 保留协议、主机、路径与查询参数名,使请求仍可被识别;
- 掩码凭据(userinfo)、查询参数值与 fragment 内容,替换为
[REDACTED ****]标记。
之所以必须"系统性"掩码,是因为AxiosError.message总是被toJSON()原样序列化进日志,而配置中的redact选项只能清理config下的键值,无法清理已经生成好的错误消息文本。单元测试 buildFullPath.test.js 验证了这一行为,例如:
'Invalid URL "https:[REDACTED ****]@api.example.com/v1?apikey=[REDACTED ****]&id=[REDACTED ****]#token=[REDACTED ****]&[REDACTED ****]": missing "//" after protocol'序列化错误:toJSON() 与 redact 脱敏配置
使用toJSON()可以获得包含更多信息的错误对象快照:
axios.get("/user/12345").catch(function (error) { console.log(error.toJSON()); });从 AxiosError.js 的toJSON()实现可以看到,返回对象包含标准字段(message、name、stack)、浏览器扩展字段(description、fileName等)以及 axios 特有字段(config、code、status)。
为了避免把密钥从error.config中打进日志,可以在请求配置里传入redact数组。调用AxiosError#toJSON()时,任何深度的、大小写不敏感的同名配置键都会被替换为脱敏标记:
axios.get("/user/12345", { headers: { Authorization: "Bearer token" }, redact: ["authorization"] }).catch(function (error) { console.log(error.toJSON().config.headers.Authorization); // [REDACTED ****] });实现上,toJSON()检测到config.redact是非空数组时,会调用redactConfig(AxiosError.js)生成脱敏后的配置快照:它把redact中的键统一转为小写后逐一匹配,递归遍历普通对象与数组,对AxiosHeaders实例先调用其toJSON()再处理,并通过seen列表短路循环引用。命中键的值被替换为模块级常量REDACTED = '[REDACTED ****]'。AxiosError.test.js 的toJSON redaction via config.redact测试组覆盖了完整语义:redact未定义或为空数组时保持旧序列化行为;顶层键、嵌套对象(auth.password、proxy.auth.password)、AxiosHeaders实例、对象数组内的键均可被正确掩码;并且对继承的redact访问器与原型污染场景做了防护。
小结:可落地的错误处理清单
结合文档与源码,可归纳出如下可复制的错误处理实践:
- 用
axios.isAxiosError(error)确认错误来源,再按error.response→error.request→ 其他 的顺序三分支处理; - 依赖
error.code而非error.message文本做类型分发:ERR_CANCELED(取消)、ETIMEDOUT/ECONNABORTED(超时)、ERR_NETWORK(网络/CORS)、ERR_BAD_REQUEST/ERR_BAD_RESPONSE(4xx/5xx); - 用
validateStatus精确控制哪些状态码算失败,例如把 404 视为"正常空结果"时返回status < 500; - 生产环境设置
timeout,并按需开启transitional.clarifyTimeoutError以获得可区分的ETIMEDOUT; - 日志输出统一走
error.toJSON(),并对Authorization、token、password等键配置redact;注意错误消息文本本身的脱敏只由 axios 在生成时完成(如畸形 URL 场景),redact无法回溯清理。
【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考