首页
/ 深入解析 Puppeteer 的 InterceptResolutionAction 枚举:协作式请求拦截的六种裁决状态

深入解析 Puppeteer 的 InterceptResolutionAction 枚举:协作式请求拦截的六种裁决状态

2026-09-07 23:17:09作者:邵娇湘

Puppeteer 的 InterceptResolutionAction 枚举定义了在一次请求拦截(Request Interception)处理流程中,请求最终"如何被裁决"的全部候选动作。它是理解 Puppeteer 协作式请求拦截(cooperative request interception)优先级机制的钥匙,也是阅读 HTTPRequest.abort()respond()continue()interceptResolutionState() 等 API 时绕不开的类型。读完本文,你将掌握每个枚举成员的确切语义、它与 InterceptResolutionState 的关系,以及 Puppeteer 如何根据优先级将多个监听器的意图收敛为唯一动作。

枚举定义总览

InterceptResolutionActionHTTPRequest.ts 的 API 定义中被声明为字符串枚举,共 6 个成员,文档入口见 docs/api/puppeteer.interceptresolutionaction.md

export enum InterceptResolutionAction {
  Abort = 'abort',
  Respond = 'respond',
  Continue = 'continue',
  Disabled = 'disabled',
  None = 'none',
  AlreadyHandled = 'already-handled',
}
枚举成员 字面值 语义
Abort "abort" 请求将被中止(配合 errorCode 上报失败原因)
Respond "respond" 请求将被本地构造的 ResponseForRequest 直接满足
Continue "continue" 请求将按原样(或携带 overrides)放行到网络
Disabled "disabled" 当前请求拦截功能未启用
None "none" 尚无任何处理者表达过意图
AlreadyHandled "already-handled" 请求已被某处理者立即处理完毕

需要注意:其中 abortrespondcontinue真正会送达浏览器的裁决结果,它们与 ActionResult 联合类型('continue' | 'abort' | 'respond')一一对应,见 ActionResult 定义;而 disablednonealready-handled 是用于描述拦截系统内部状态的"非动作"值。这也是该枚举在类型体系上要区分于 ActionResult 的原因。

六个成员背后的机制含义

要理解每个成员的真实含义,需要结合拦截器内部的状态机。每个 HTTPRequest 在构造时都会持有如下内部拦截状态(见 HTTPRequest 内部状态):

interception = {
  enabled: false,        // 拦截是否开启
  handled: false,        // 是否已被立即处理
  handlers: [],          // 待执行的异步处理队列
  resolutionState: {
    action: InterceptResolutionAction.None,  // 当前累积的裁决意图
  },
  requestOverrides: {},  // continue 的覆盖项
  response: null,        // respond 的响应体
  abortReason: null,     // abort 的错误原因
};

None:尚未表达任何意图

当拦截已启用、但还没有任何 request 监听器对当前请求调用过 abort/respond/continue(且都未携带 priority)时,裁决状态停留在 None。它是请求监听器被逐个执行过程中的"中间态",表明"意图未定"。这一点可以从 interceptResolutionState() 的实现中得到印证——当 enabled 为 true 且 handled 为 false 时,它直接返回内部的 resolutionState,初始值即 {action: None}方法实现)。

Continue:放行请求

当任一处理者调用 request.continue(overrides, priority) 且其优先级胜出后,状态被置为 Continue。最终请求会带着累计的 requestOverrides(例如改写过的请求头)继续发往网络。放行的判断逻辑位于 continue() 实现:当新调用的 priority 大于当前记录的最高优先级时直接覆盖;等于最高优先级时,abort/respond 不会被 continue 顶掉,仅当现行动作不是这两者时才会改为 Continue

Respond:本地伪造响应

当处理者调用 request.respond(response, priority) 且意图胜出,状态变为 Respond。浏览器不会真的发起网络请求,而是使用处理者提供的 Partial<ResponseForRequest>(状态码、头部、contentType、body)直接满足该请求。响应内容暂存于 interception.response,最终裁决时由 finalizeInterceptions() 读取并交给 _respond()respond() 优先级比较finalizeInterceptions 分发)。

Abort:中止请求

当处理者调用 request.abort(errorCode, priority) 且意图胜出,状态变为 Abort。请求不会到达服务器,而是以指定的 ErrorCode(如 'aborted''blockedbyclient''failed' 等 14 种)映射为 CDP 的 Network.ErrorReason 后中止,页面会收到 requestfailed 事件。注意 abort同优先级比较时拥有相对更高的"固执性"——其实现条件是 priority >= this.interception.resolutionState.priority(大于等于即可覆盖,见 abort() 实现),而 continue/respond 在相等优先级时还会再细分处置,这正是测试套件中"平票时 abort 优先"行为的基础。

Disabled:拦截功能未开启

如果从未对页面调用 page.setRequestInterception(true),则该请求的 enabled 标志为 false,此时 interceptResolutionState() 直接返回 {action: Disabled},不会去读内部状态。这一分支排在 interceptResolutionState() 实现的最前面(源码)。

AlreadyHandled:已被立即处理

当某个处理者调用了不带 prioritycontinue/respond/abort 时,请求会被"立即处理"(immediately handled),内部的 handled 置为 true。此后任何处理者再查询状态,得到的都是 AlreadyHandled,并且再调用拦截方法会因 verifyInterception() 中的 'Request is already handled!' 断言而抛错(verifyInterception 实现)。对应的公开查询方法是 isInterceptResolutionHandled()

裁决状态如何在调用链中流转

abortrespondcontinue 三者的实际效果都汇聚在 finalizeInterceptions() 中完成:它先按入队顺序 Promise 链式执行完所有挂起的异步处理函数,清空 handlers,再依据最终 actionswitch 分发到 _abort()_respond()_continue()

const {action} = this.interceptResolutionState();
switch (action) {
  case 'abort':
    return await this._abort(this.interception.abortReason);
  case 'respond':
    if (this.interception.response === null) {
      throw new Error('Response is missing for the interception');
    }
    return await this._respond(this.interception.response);
  case 'continue':
    return await this._continue(this.interception.requestOverrides);
}

从这段代码可以清晰地推断出:拦截系统是先"收集多个处理者的意图、按优先级收敛成一个 action",再"统一执行"。这也是协作式拦截与旧式"先到先得、立即处理"模式最根本的区别。

使用方式:立即处理 vs. 协作裁决

continuerespondabort 三个方法的第二个可选参数 priority 是决定走哪条路径的关键开关,其注释在 continue() 参数文档中明确说明:如果提供了 priority,拦截按协作式规则裁决;否则立即处理

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setRequestInterception(true);   // 开启拦截的前提

// 监听器 A:样式表一律以本地内容响应,优先级 0
page.on('request', request => {
  if (request.url().endsWith('.css')) {
    void request.respond({
      status: 200,
      contentType: 'text/css',
      body: '* { color: red }',
    }, 0);
  } else {
    void request.continue({}, 0);
  }
});

// 监听器 B:广告域名一律中止,优先级 10(更高,获胜)
page.on('request', request => {
  if (request.url().includes('ads.example.com')) {
    void request.abort('blockedbyclient', 10);
  } else {
    void request.continue({}, 0);
  }
});

对于跨浏览器(CDP / WebDriver BiDi)支持:该枚举同时被 CDP 侧 HTTPRequestBiDi 侧 HTTPRequest 引用,因此它是 Puppeteer 屏蔽协议差异的统一裁决语言。

优先级裁决规则速查

结合 continue()respond()abort() 的实现,可将协作式规则归纳如下:

  • priority 调用 → 立即处理,后续监听器看到 AlreadyHandled
  • 新调用 priority > 已记录最高值 → 直接覆盖为对应 action(abort 条件为 >=);
  • 新调用 priority == 当前最高值(平票):
    • 现有 Abortrespond/continueAbort 保持(respond 分支中见 源码,continue 分支中见 源码);
    • 现有 RespondcontinueRespond 保持,continue 仅记录 overrides;
    • 两方同为 continue:仍为 Continue
  • 默认基线优先级由 DEFAULT_INTERCEPT_RESOLUTION_PRIORITY 给出,其值为 0(见 常量定义)。

测试 test/src/requestinterception-experimental.test.ts 中的 should cooperatively ${expectedAction} by priority 用例直观演示了这一规则:三个监听器分别对同一 .css 请求以 0/1 的优先级调用 continuerespondabort,最终只产生一个动作,并断言结果与获得最高优先级的那一方一致。

如何观测当前裁决状态

对外暴露状态查询的是两个实例方法:

测试中的用例 should indicate already-handled if an intercept has been handled 验证了二者的一致性:第一个监听器对请求执行 request.continue()(立即处理),随后的监听器再查询 isInterceptResolutionHandled() 得到 trueinterceptResolutionState().action 得到 InterceptResolutionAction.AlreadyHandled

小结

InterceptResolutionAction 虽然只是一个仅有 6 个成员的枚举,却是串联 Puppeteer 请求拦截 API(continuerespondabort)与底层状态机的核心类型:三个"动作型"成员代表最终交付给浏览器的结果,三个"状态型"成员描述系统所处阶段。理解它们的区别以及配合 priority 的收敛规则,是编写可靠的多监听器请求拦截逻辑(如广告屏蔽、资源 mock、请求改写混用)的前提。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.79 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
390