Skip to main content

网络 (Network)

Playwright 提供 API 来 监控 和 修改 网络流量,包括 HTTP 和 HTTPS。页面执行的任何请求,包括 XHRs 和 fetch 请求,都可以被跟踪、修改和处理。

HTTP 身份验证​

使用 browser.newContext([options]) 执行 HTTP 身份验证。

const context = await browser.newContext({
httpCredentials: {
username: 'bill',
password: 'pa55w0rd',
},
});
const page = await context.newPage();
await page.goto('https://example.com');

HTTP 代理​

您可以配置页面通过 HTTP(S) 代理或 SOCKSv5 加载。代理可以为整个浏览器全局设置,也可以为每个浏览器上下文单独设置。

您可以选择为 HTTP(S) 代理指定用户名和密码,也可以指定要绕过代理的主机。

这是一个全局代理的示例:

const browser = await chromium.launch({
proxy: {
server: 'http://myproxy.com:3128',
username: 'usr',
password: 'pwd'
}
});

当为每个上下文单独指定代理时,Windows 上的 Chromium 需要一个提示,表明将设置代理。这是通过将非空代理服务器传递给浏览器本身来完成的。这是一个特定于上下文的代理示例:

const browser = await chromium.launch({
// Windows 上的 Chromium 需要浏览器代理选项。
proxy: { server: 'per-context' }
});
const context = await browser.newContext({
proxy: { server: 'http://myproxy.com:3128' }
})

网络事件​

您可以监控所有 Request 和 Response:

const { chromium, webkit, firefox } = require('playwright');

(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();

// 订阅 'request' 和 'response' 事件。
page.on('request', request =>
console.log('>>', request.method(), request.url()));
page.on('response', response =>
console.log('<<', response.status(), response.url()));
await page.goto('https://example.com');

await browser.close();
})();

或者在按钮点击后使用 page.waitForResponse(urlOrPredicate[, options]) 等待网络响应:

// 使用 glob URL 模式
const [response] = await Promise.all([
page.waitForResponse('**/api/fetch_data'),
page.getByText('Update').click(),
]);

变体​

使用 page.waitForResponse(urlOrPredicate[, options]) 等待 Response

// 使用 RegExp
const [response] = await Promise.all([
page.waitForResponse(/\.jpeg$/),
page.getByText('Update').click(),
]);

// 使用接受 Response 对象的谓词
const [response] = await Promise.all([
page.waitForResponse(response => response.url().includes(token)),
page.getByText('Update').click(),
]);

处理请求​

await page.route('**/api/fetch_data', route => route.fulfill({
status: 200,
body: testData,
}));
await page.goto('https://example.com');

您可以通过处理 Playwright 脚本中的网络请求来 mock API 端点。

变体​

使用 browserContext.route(url, handler[, options]) 在整个浏览器上下文上设置路由,或使用 page.route(url, handler[, options]) 在页面上设置路由。它将应用于弹出窗口和打开的链接。

await browserContext.route('**/api/login', route => route.fulfill({
status: 200,
body: 'accept',
}));
await page.goto('https://example.com');

修改请求​

// 删除 header
await page.route('**/*', route => {
const headers = route.request().headers();
delete headers['X-Secret'];
route.continue({headers});
});

// 继续请求作为 POST。
await page.route('**/*', route => route.continue({method: 'POST'}));

您可以继续请求并进行修改。上面的示例从传出请求中删除了 HTTP header。

中止请求​

您可以使用 page.route(url, handler[, options]) 和 route.abort([errorCode]) 中止请求。

await page.route('**/*.{png,jpg,jpeg}', route => route.abort());

// 根据请求类型中止
await page.route('**/*', route => {
return route.request().resourceType() === 'image' ?
route.abort() : route.continue();
});

修改响应​

要修改响应,请使用 APIRequestContext 获取原始响应,然后将响应传递给 route.fulfill([options])。您可以通过选项覆盖响应上的各个字段:

await page.route('**/title.html', async route => {
// 获取原始响应。
const response = await page.request.fetch(route.request());
// 向标题添加前缀。
let body = await response.text();
body = body.replace('<title>', '<title>My prefix:');
route.fulfill({
// 传递响应中的所有字段。
response,
// 覆盖响应体。
body,
// 强制内容类型为 html。
headers: {
...response.headers(),
'content-type': 'text/html'
}
});
});

录制和重放请求​

您可以将网络活动录制为 HTTP Archive 文件 (HAR)。稍后,此存档可用于 mock 网络请求的响应。您需要:

  1. 录制 HAR 文件。
  2. 将 HAR 文件与测试一起提交。
  3. 在测试中使用保存的 HAR 文件路由请求。

使用 CLI 录制 HAR​

使用 Playwright CLI 打开浏览器并传递 --save-har 选项以生成 HAR 文件。或者,使用 --save-har-glob 仅保存您感兴趣的请求,例如 API 端点。如果 har 文件名以 .zip 结尾,则工件将作为单独的文件写入,并全部压缩到一个 zip 中。

# 将来自 example.com 的 API 请求保存为 "example.har" 存档。
npx playwright open --save-har=example.har --save-har-glob="**/api/**" https://example.com

使用脚本录制 HAR​

或者,您可以以编程方式录制 HAR,而不是使用 CLI。在使用 browser.newContext([options]) 创建 BrowserContext 时传递 har 选项以创建存档。如果 har 文件名以 .zip 结尾,则工件将作为单独的文件写入,并全部压缩到一个 zip 中。

const context = await browser.newContext({
recordHar: { path: 'example.har', urlFilter: '**/api/**' }
});

// ... 执行操作 ...

// 关闭上下文以确保 HAR 保存到磁盘。
await context.close();

从 HAR 重放​

使用 page.routeFromHAR(har[, options]) 或 browserContext.routeFromHAR(har[, options]) 从 HAR 文件提供匹配的响应。

// 从 HAR 重放 API 请求。
// 使用 HAR 中的匹配响应,
// 或者如果没有匹配项则中止请求。
await page.routeFromHAR('example.har');

HAR 重放严格匹配 URL 和 HTTP 方法。对于 POST 请求,它还严格匹配 POST 有效负载。如果多个录制匹配一个请求,则选择具有最多匹配 header 的那个。导致重定向的条目将自动跟随。

与录制时类似,如果给定的 HAR 文件名以 .zip 结尾,则将其视为包含 HAR 文件以及存储为单独条目的网络有效负载的存档。您也可以解压缩此存档,手动编辑有效负载或 HAR 日志,并指向解压缩的 har 文件。所有有效负载都将相对于文件系统上的解压缩 har 文件进行解析。

WebSockets​

Playwright 开箱即用地支持 WebSockets 检查。每次创建 WebSocket 时,都会触发 page.on('websocket') 事件。此事件包含 WebSocket 实例,用于进一步检查 web socket 帧:

page.on('websocket', ws => {
console.log(`WebSocket opened: ${ws.url()}>`);
ws.on('framesent', event => console.log(event.payload));
ws.on('framereceived', event => console.log(event.payload));
ws.on('close', () => console.log('WebSocket closed'));
});

缺失的网络事件和 Service Workers​

Playwright 的内置 browserContext.route(url, handler[, options]) 和 page.route(url, handler[, options]) 允许您的测试原生路由请求并执行 mock 和拦截。

  1. 如果您使用的是 Playwright 的原生 browserContext.route(url, handler[, options]) 和 page.route(url, handler[, options]),并且似乎缺少网络事件,请通过将 Browser.newContext.serviceWorkers 设置为 'block' 来禁用 Service Workers。
  2. 您可能正在使用 mock 工具,例如 Mock Service Worker (MSW)。虽然此工具开箱即用地用于 mock 响应,但它添加了自己的 Service Worker 来接管网络请求,从而使它们对 browserContext.route(url, handler[, options]) 和 page.route(url, handler[, options]) 不可见。如果您对网络测试和 mock 都感兴趣,请考虑使用内置的 browserContext.route(url, handler[, options]) 和 page.route(url, handler[, options]) 进行 响应 mock。
  3. 如果您不仅对使用 Service Workers 进行测试和网络 mock 感兴趣,而且对路由和监听 Service Workers 本身发出的请求感兴趣,请参阅 此实验性功能。