Puppeteer HTTPRequest.abortErrorReason() 详解:请求拦截中止原因的读取与底层实现

发布时间:2026/9/7 7:50:11
Puppeteer HTTPRequest.abortErrorReason() 详解:请求拦截中止原因的读取与底层实现 Puppeteer HTTPRequest.abortErrorReason() 详解请求拦截中止原因的读取与底层实现【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer导读HTTPRequest.abortErrorReason()是 Puppeteer本项目为 JavaScript API for Chrome and Firefox在请求拦截体系中用于**读取最近一次请求被中止的原因**的核心查询方法返回 Chrome DevTools Protocol 的Network.ErrorReason枚举值或null。本文将以官方 API 文档 docs/api/puppeteer.httprequest.aborterrorreason.md 为骨架结合 HTTPRequest.ts 源码与 请求拦截实验性测试 的实现事实讲清它的签名、返回值的写入时机、与abort()/ErrorCode的映射关系以及基于优先级的协作式拦截中如何用它做诊断与二次决策。一、方法定位请求拦截三原语之一的状态查询器在 Puppeteer 的请求拦截模型中被拦截的HTTPRequest共有三种处置方式continue()放行并可选改写请求、respond()直接伪造响应、abort()中止请求。abortErrorReason()正是服务于abort这条路径的读数接口。该 API 文档只给出了最精炼的声明功能描述The most recent reason for aborting the request返回最近一次请求中止的原因方法签名class HTTPRequest { abortErrorReason(): Protocol.Network.ErrorReason | null; }返回类型Protocol.Network.ErrorReason | null这里的Protocol.Network.ErrorReason来自 Chrome DevTools Protocol 的Network域枚举。返回null表示该请求尚未或没有被记录过中止原因——从源码结构看请求在未被中止或命中其它拦截路径时该字段始终为空。相关 API 文档HTTPRequest 概览、HTTPRequest.abort()、Page.setRequestInterception()。二、源码实现abortReason 存放在哪里在 HTTPRequest.ts 中每个请求实例维护着一个内部interception状态对象abortErrorReason()读取的正是其中的abortReason字段protected interception: { enabled: boolean; handled: boolean; handlers: Array() void | PromiseLikeany; resolutionState: InterceptResolutionState; requestOverrides: ContinueRequestOverrides; response: PartialResponseForRequest | null; abortReason: Protocol.Network.ErrorReason | null; } { enabled: false, handled: false, handlers: [], resolutionState: {action: InterceptResolutionAction.None}, requestOverrides: {}, response: null, abortReason: null, // 初始值为 null };而abortErrorReason()的实现则是一行直读HTTPRequest.ts/** * The most recent reason for aborting the request */ abortErrorReason(): Protocol.Network.ErrorReason | null { return this.interception.abortReason; }由此可以推断两个关键事实abortReason是每请求独立记录的实例状态随 HTTPRequest 对象生灭不会跨请求共享它的最近一次语义体现在同一次拦截生命周期中若多次以abort()设置原因后一次写入会覆盖前一次见下文abort()的赋值逻辑。三、中止原因从哪来abort() 与 ErrorCode 的映射链abortReason只在调用abort()时被写入。HTTPRequest.abort() 的完整签名是abort(errorCode?: ErrorCode, priority?: number): Promisevoid;其中errorCode是 Puppeteer 面向用户的公共错误码小写连写风格默认值为failed。在 HTTPRequest.ts 中abort()会先把用户友好的ErrorCode翻译成 CDP 的Network.ErrorReason再写入状态async abort(errorCode: ErrorCode failed, priority?: number): Promisevoid { this.verifyInterception(); if (!this.canBeIntercepted()) { return; } const errorReason errorReasons[errorCode]; assert(errorReason, Unknown error code: errorCode); if (priority undefined) { return await this._abort(errorReason); } this.interception.abortReason errorReason; if ( this.interception.resolutionState.priority undefined || priority this.interception.resolutionState.priority ) { this.interception.resolutionState { action: InterceptResolutionAction.Abort, priority, }; return; } }从这段源码可以得出关于abortErrorReason()返回值的重要行为差异立即模式不带priorityabort()直接把errorReason交给底层_abort()执行不写入interception.abortReason此时调用abortErrorReason()仍返回null协作式模式带priorityabort()先把原因写入interception.abortReason再按优先级规则更新resolutionState请求真正被中止要等到拦截终结finalizeInterceptions()时统一派发。也就是说abortErrorReason()主要是为**基于优先级的协作式拦截cooperative interception**场景设计的诊断接口。测试 requestinterception-experimental.test.ts 正是通过request.abort(failed, 0)后读取abortErrorReason()并断言其等于Failed来验证该行为的it(should be able to access the error reason, async () { const {page, server} await getTestState(); await page.setRequestInterception(true); page.on(request, request { void request.abort(failed, 0); }); let abortReason null; page.on(request, request { abortReason request.abortErrorReason(); void request.continue({}, 0); }); await page.goto(server.EMPTY_PAGE).catch(() {}); expect(abortReason).toBe(Failed); });注意该测试中两个request监听器先后注册第一个先以优先级0发起abort第二个再通过abortErrorReason()读回已记录的原因验证了最近一次中止原因可查询这一语义。四、ErrorCode → ErrorReason 全量映射表errorCodeErrorCode 类型定义共 14 个取值与 CDPNetwork.ErrorReason的对应关系定义在 HTTPRequest.ts 的errorReasons常量表中原文完整如下const errorReasons: RecordErrorCode, Protocol.Network.ErrorReason { aborted: Aborted, accessdenied: AccessDenied, addressunreachable: AddressUnreachable, blockedbyclient: BlockedByClient, blockedbyresponse: BlockedByResponse, connectionaborted: ConnectionAborted, connectionclosed: ConnectionClosed, connectionfailed: ConnectionFailed, connectionrefused: ConnectionRefused, connectionreset: ConnectionReset, internetdisconnected: InternetDisconnected, namenotresolved: NameNotResolved, timedout: TimedOut, failed: Failed, } as const;整理成速查表公共错误码ErrorCodeabort 参数返回值abortErrorReason()典型场景abortedAborted请求已被主动中止accessdeniedAccessDenied访问被拒绝如权限不足addressunreachableAddressUnreachable目标地址不可达blockedbyclientBlockedByClient被客户端策略屏蔽如广告拦截器行为blockedbyresponseBlockedByResponse因响应策略如 X-Frame-Options被阻断connectionabortedConnectionAborted连接被中止connectionclosedConnectionClosed连接被关闭connectionfailedConnectionFailed连接失败connectionrefusedConnectionRefused连接被拒绝connectionresetConnectionReset连接被重置RSTinternetdisconnectedInternetDisconnected网络已断开namenotresolvedNameNotResolvedDNS 无法解析域名timedoutTimedOut请求超时failedFailed通用失败abort 的默认错误码事实提示以上 14 个枚举值及映射完全取自 errorReasons 常量表是本仓库当前的确定行为。若传入表外字符串abort()会通过assert(errorReason, Unknown error code: errorCode)立即抛出 Unknown error code 错误。这些ErrorReason最终会被浏览器转换为对应的网络错误文本。测试 requestinterception-experimental.test.ts 用自定义错误码internetdisconnected验证了这一点——被中止请求的failure().errorText为net::ERR_INTERNET_DISCONNECTED而源码注释见failure()的文档也给出通用失败会呈现为net::ERR_FAILED之类的人类可读文本的说明。五、写入与生效时机协作式拦截的终结流程为什么要在意abortErrorReason()而非直接在监听器里记变量关键在于请求被中止的最终派发发生在拦截终结阶段。在 HTTPRequest.ts 的finalizeInterceptions()中async finalizeInterceptions(): Promisevoid { await this.interception.handlers.reduce((promiseChain, interceptAction) { return promiseChain.then(interceptAction); }, Promise.resolve()); this.interception.handlers []; const {action} this.interceptResolutionState(); switch (action) { case abort: return await this._abort(this.interception.abortReason); case respond: // ... return await this._respond(this.interception.response); case continue: // ... return await this._continue(this.interception.requestOverrides); } }整个过程是enqueueInterceptAction()把所有异步拦截处理器压入interception.handlers队列 → 依次执行每个处理器内部各自调用abort/respond/continue并更新resolutionState→ 队列清空后按最终决议动作统一落地。当决议动作是abort时真正传给底层_abort()的正是this.interception.abortReason——即abortErrorReason()返回的那个值。由此abortErrorReason()的工程价值非常清晰在多个 handler / 多监听器叠加、且以不同priority竞争处置权时它能在最终派发前告诉你当前胜出方案是不是 abort、以及用的是哪个原因配合 interceptResolutionState()返回InterceptResolutionAction与priority和 isInterceptResolutionHandled()可以完整刻画一次请求当前的拦截决议快照决议动作abort对应 InterceptResolutionAction.Abort同枚举还有Respond、Continue、Disabled、None、AlreadyHandled。六、实战用法与代码示例6.1 基础用法按资源类型中止请求仓库自带示例 examples/block-images.js 展示了最常见的abort()场景——屏蔽图片资源同时放行其余请求import puppeteer from puppeteer; const browser await puppeteer.launch(); const page await browser.newPage(); await page.setRequestInterception(true); page.on(request, request { if (request.resourceType() image) { request.abort(); } else { request.continue(); } }); await page.goto(https://news.google.com/news/); await page.screenshot({path: news.png, fullPage: true}); await browser.close();这里request.abort()未传参等价于request.abort(failed)底层原因即Failed。6.2 协作式拦截在监听器内诊断中止原因当启用带优先级的协作式拦截时可以在后续处理中查询 最近一次中止原因从而决定是保持中止还是降级放行await page.setRequestInterception(true); page.on(request, request { // 处理链路的某个环节以优先级 0 请求中止 if (request.url().endsWith(.css)) { void request.abort(blockedbyclient, 0); } else { void request.continue({}, 0); } }); // 在另一个监听器或后续处理器中读取决议原因 page.on(request, request { const reason request.abortErrorReason(); // BlockedByClient | null if (reason) { console.log(request will be aborted due to: ${reason}); } });对应到上文测试用例最终该请求会以requestfailed事件收尾读者可在页面侧通过requestfailed监听、request.failure().errorText拿到浏览器级错误文本与abortErrorReason()返回的协议级枚举互为印证。6.3 注意事项依据源码实现得出的行为约束必须先开启拦截abort()内部调用verifyInterception()HTTPRequest.ts当page.setRequestInterception(true)未开启时抛出 Request Interception is not enabled!未开启拦截时自然也就谈不上abortErrorReason()的有效取值。不是所有请求都可被拦截abort()前会检查canBeIntercepted()不可拦截的请求会被静默跳过abortReason不会更新。null并不代表没失败返回null只表示interception.abortReason尚未被写入例如立即模式abort、或决议动作是continue/respond。判断请求最终是否失败应改用 failure()返回{errorText: string} | null与requestfailed事件。默认值约定不传errorCode即为failed这是 Puppeteer 保证传入abortErrorReason()有确定默认语义的兜底设计。七、小结围绕官方文档HTTPRequest.abortErrorReason() 方法本文用源码与测试补齐了它背后的完整链路层面结论依据对外语义返回最近一次中止原因类型Protocol.Network.ErrorReason \| nullAPI 文档存储位置HTTPRequest.interception.abortReason初始nullHTTPRequest.ts写入时机abort()携带priority的协作式路径中写入HTTPRequest.ts取值来源abort()的公共ErrorCode经errorReasons常量表翻译而来共 14 个HTTPRequest.ts派发时机finalizeInterceptions()按最终决议动作把abortReason传给_abort()HTTPRequest.ts验证用例request.abort(failed, 0)后读到Failedinternetdisconnected对应net::ERR_INTERNET_DISCONNECTEDrequestinterception-experimental.test.ts理解abortErrorReason()就掌握了 Puppeteer 请求拦截中谁以什么原因中止了这条请求这一关键状态的可观测入口——它是编写多处理器协作式请求拦截、以及在测试中断言拦截行为时最直接、最不易出错的依据。【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考