首页
/ Puppeteer HTTPResponse.status() 详解:从响应状态码读取到 CDP/BiDi 底层实现

Puppeteer HTTPResponse.status() 详解:从响应状态码读取到 CDP/BiDi 底层实现

2026-09-07 11:51:54作者:何举烈Damon

HTTP 状态码是网络自动化中最基础的判定信号之一。本文以 Puppeteer 的 HTTPResponse.status() 方法为主线,讲解如何在页面导航与网络拦截场景中读取响应状态码,并结合当前仓库中 CDP 与 WebDriver BiDi 两套后端的真实实现,揭示状态码从浏览器协议事件到 Puppeteer 对象的传递路径。读完本文,你将掌握通过 status() 判断请求成败、区分重定向与缓存命中的方法,并理解它和 ok()statusText() 等姊妹方法之间的配合关系。

HTTPResponse.status() 方法语义与签名

在 Puppeteer 中,HTTPResponse 类代表页面收到的 HTTP 响应,官方文档将其描述为"由 Page 类接收到的响应"。状态码方法定义如下:

class HTTPResponse {
  abstract status(): number;
}
  • 返回类型number,即响应状态码,例如成功时为 200
  • 声明方式abstract,意味着 HTTPResponse 是一个抽象基类,具体数值由不同协议后端的子类实现;
  • 语义:返回的是响应的状态码(status code),如 200(成功)、301/302(重定向)、404(未找到)、500(服务器内部错误)等。

从源码看,该抽象方法定义于 packages/puppeteer-core/src/api/HTTPResponse.ts#L54-L57,其 JSDoc 注释与文档完全一致:"The status code of the response (e.g., 200 for a success)." 也就是说,这份 API 文档中的描述正是直接提取自源码注释,二者保持同步。

如何在实战中获得一个 HTTPResponse 并读取状态码

status() 的调用对象是 HTTPResponse,Puppeteer 通常通过以下三种途径把它交到开发者手中,均以 response 事件或 Promise 的形式出现:

1. 页面导航的返回值

page.goto()page.reload()page.goBack()/page.goForward() 等导航类方法在成功完成导航后会返回主导航响应(若发生重定向,则返回最终响应的 HTTPResponse,同时可通过 response.request().redirectChain() 回溯中间响应):

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

const response = await page.goto('https://example.com');
if (response) {
  console.log(`最终状态码:${response.status()}`); // 例如 200
  console.log(`是否成功:${response.ok()}`); // true
}
await browser.close();

2. waitForResponse() 等待指定请求

配合 page.waitForResponse() 可以精确等待某个请求并拿到其响应,这是接口轮询、异步加载场景下最常用的做法:

const page = await browser.newPage();

// 先挂起等待器,再触发可能产生该请求的动作
const responsePromise = page.waitForResponse(
  res => res.url().includes('/api/user') && res.status() === 200,
);
await page.click('#load-user');
const response = await responsePromise;

console.log(response.status()); // 200

注意上面谓词函数中正是用 res.status() 过滤"状态码必须为 200"的响应,可见 status() 经常作为 waitForResponse 的判定条件使用。

3. 监听 page 的 response 事件

页面发起的所有子资源请求(图片、脚本、XHR 等)都会触发 response 事件,可以结合 URL 与状态码做全局巡检或性能埋点:

page.on('response', response => {
  console.log(`${response.status()} ${response.url()}`);
});

源码级剖析:CDP 后端的 status() 数值从何而来

Puppeteer 同时支持 Chrome DevTools Protocol(CDP)与 WebDriver BiDi 两种自动化协议,status() 也因此存在两个具体实现。先看默认的 CDP 实现,文件位于 packages/puppeteer-core/src/cdp/HTTPResponse.ts

CdpHTTPResponse 的构造函数中,状态码并非简单取网络事件字段,而是存在一个优先级问题

this.#status = extraInfo ? extraInfo.statusCode : responsePayload.status;
  • responsePayload 来自 CDP 的 Network.responseReceived 事件;
  • extraInfo 来自 Network.responseReceivedExtraInfo 事件(ResponseReceivedExtraInfoEvent)。

extraInfo 可用时,优先采用 extraInfo.statusCode,否则退回 responsePayload.status。这一设计的价值在于:responseReceivedExtraInfo 携带的是更完整、经过额外信息合并后的响应数据,能够规避某些跨域(CORS)或鉴权场景下早期事件信息不完整的问题。

而最终对外暴露的实现极其简洁——返回构造阶段就已确定并缓存好的私有字段:

override status(): number {
  return this.#status;
}

(见 packages/puppeteer-core/src/cdp/HTTPResponse.ts#L103-L105)。这意味着一份响应对象的 status() 是构造时快照,不会随响应生命周期变化,多次调用结果恒定。

BiDi 后端的 status() 实现

当通过 WebDriver BiDi 协议连接 Firefox 或支持 BiDi 的 Chrome 时,响应对象由 BidiHTTPResponse 提供,实现位于 packages/puppeteer-core/src/bidi/HTTPResponse.ts

override status(): number {
  return this.#data.status;
}

这里 #data 是 WebDriver BiDi 协议中 network.ResponseData 类型的响应数据,状态码直接取自该结构体的 status 字段。值得注意的是,BidiHTTPResponse 通过静态工厂方法 from() 管理实例:如果同一请求已存在响应对象,会用最新数据更新已有对象并复用,而不是创建新实例。

从两套实现可以看出一个共同结论:status() 只是对浏览器侧已解析好的状态码做一次透传读取,本身不参与任何重定向合并、缓存判定等逻辑——那些语义由上层方法(如 ok()fromCache())承担。

与 status() 紧密相关的姊妹方法

理解 status() 时,最好同时了解它周边几个方法的分工,这些方法同样定义在 packages/puppeteer-core/src/api/HTTPResponse.ts 的抽象基类中。

ok():把状态码折叠成布尔值

HTTPResponse 基类中直接给出了 ok() 的完整实现(非抽象),packages/puppeteer-core/src/api/HTTPResponse.ts#L46-L52

ok(): boolean {
  const status = this.status();
  return status === 0 || (status >= 200 && status <= 299);
}

从中可以读出两层关键信息:

  1. ok() 内部正是调用 this.status() 判断的,对应文档所述"status in the range 200-299"(见 HTTPResponse.ok() 文档);
  2. 一个值得注意的特例:状态码 0 也被视为成功。这是因为某些非 HTTP 资源(如 data:file: 等协议或个别特殊场景)不会产生标准 HTTP 状态码,浏览器上报的 status 为 0,此时不应武断地判定为失败。所以判断"响应是否成功",用 ok() 比手写 status() >= 200 && status() < 300 更稳妥。

statusText():状态码的人类可读描述

status() 并列的还有 statusText(),返回如 "OK""Not Found" 之类的状态文本(见 HTTPResponse.statusText() 文档)。在 CDP 实现中,状态文本的解析同样有细节:若存在 extraInfo.headersText,会尝试从响应起始行(status line)中正则提取状态文本,否则回退到 responsePayload.statusText

status()statusText() 的关系:前者是可编程判定用的数值,后者更适合日志输出与人类阅读。例如状态码 404 对应状态文本 "Not Found"。测试仓库中也存在直接断言状态文本的用例,如 requestinterception-experimental.test.ts 中期望 status()422statusText()'Unprocessable Entity'

其他相关判定方法

  • fromCache():是否命中浏览器磁盘/内存缓存。命中缓存时状态码常见为 304(配合条件请求)或被浏览器直接以 200 (from disk cache) 形式返回;
  • fromServiceWorker():是否由 Service Worker 提供服务;
  • request():通过 response.request().redirectChain() 可以拿到中间跳转响应列表,从而把 301/302 与最终 200 串成完整链路。

仓库测试如何验证 status() 的实际取值

当前仓库的集成测试为我们提供了状态码语义的直接证据,非常适合作为理解 status() 行为边界的参考:

  • test/src/navigation.test.ts 中,大量用例通过 expect(response.status()).toBe(200) 断言正常导航返回 200;同时也覆盖了失败路径——期望 response.ok()falseresponse.status()404500 的断言,印证了"非 2xx 状态码对应 ok() === false"的行为;
  • test/src/network.test.ts 中,缓存命中场景断言 HTML 响应的 status()304,而其余资源为 200,说明浏览器协商缓存返回的 304 状态码会被如实透传,不会因为"成功加载"而被改写为 200;
  • 重定向场景中,测试断言 redirected.status()302,说明通过特定请求对象拿到的响应可以单独观测到中间跳转的状态码(见 test/src/network.test.tsredirected 相关断言);
  • 单元测试 packages/puppeteer-core/src/cdp/HTTPResponse.test.ts 则直接构造 CdpHTTPResponse 实例验证响应头与状态字段的初始化逻辑,可以看到构造入参中 status: 200statusText: 'OK'#status 快照存储的直接对应关系。

这些测试表明:Puppeteer 不会对状态码做任何"美化",浏览器给出什么就是什么——304、302、401、404、422、500 都会被原样返回,真正的"成功与否"判断应交给 ok() 或在 status() 之上自行定义规则。

结合 status() 的实战建议与注意事项

综合 API 文档、源码与测试,给出以下可直接落地的工程建议:

1. 导航判空后再调用。 page.goto() 在页面加载失败(如连接被拒绝)时返回 null,此时应判空后再访问 status(),避免对 null 调用方法抛错:

const response = await page.goto(url).catch(() => null);
if (response && response.ok()) {
  // 处理 2xx 内容
} else if (response) {
  console.error(`加载失败:HTTP ${response.status()}`);
}

2. 健康检查不要只看 2xx。 若业务需要"到达目标页面"而不关心具体业务状态,可显式处理重定向(3xx)与缓存(304):

const response = await page.goto(url);
const code = response?.status();
if (code === 304) {
  // 协商缓存命中,需配合 fromCache()/请求头判断内容是否可用
}

3. 拦截/模拟响应时主动指定状态码。 在使用请求拦截(setRequestInterception)做接口 Mock 时,request.respond() 的第一个参数就包含 status 字段,可模拟任意状态码来验证前端分支逻辑(对应测试中出现 201422401 等状态码的断言):

await page.setRequestInterception(true);
page.on('request', request => {
  if (request.url().includes('/api/order')) {
    request.respond({
      status: 201,
      contentType: 'application/json',
      body: JSON.stringify({id: 1}),
    });
  } else {
    request.continue();
  }
});

4. 区分"状态码异常"与"协议层失败"。 status() 只反映 HTTP 状态码;如果请求从未到达服务器(如 DNS 解析失败、超时被中断),Puppeteer 可能不会产生响应对象或直接抛出导航错误。这类错误需要借助 page.on('requestfailed') 或 try/catch 处理,不要误以为 status() 会返回某种"错误码"。

5. 跨协议一致性。 无论使用 CDP(Chrome)还是 WebDriver BiDi(Firefox)连接,status() 的对外契约一致(返回 number),只是内部数据来源不同(CDP 的 Network.responseReceived 事件字段、BiDi 的 network.ResponseData.status)。因此可以放心把基于 status() 编写的巡检逻辑复用到不同浏览器上,无需关心底层协议差异。

小结

HTTPResponse.status() 是 Puppeteer 网络层 API 中最常被使用的方法之一:它语义简单(返回原始 HTTP 状态码)、成本极低(读取构造时缓存好的快照),但价值巨大——是 ok()、缓存判定、重定向追踪乃至 waitForResponse 过滤逻辑的基石。若要进一步深入,推荐按以下路径阅读仓库源码:抽象基类 HTTPResponseCDP 实现 CdpHTTPResponseBiDi 实现 BidiHTTPResponse,并结合 navigation.test.tsnetwork.test.ts 中的断言理解真实取值边界。

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

项目优选

收起
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
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391