跳到主要内容

网络

简介

Playwright 提供了 API 来监控修改浏览器的网络流量,包括 HTTP 和 HTTPS。页面发出的任何请求,包括 XHRsfetch 请求,都可以被追踪、修改和处理。

模拟 API

查看我们的 API 模拟指南 了解更多关于如何:

  • 模拟 API 请求而不实际调用 API
  • 执行 API 请求并修改响应
  • 使用 HAR 文件来模拟网络请求

网络模拟

你不需要进行任何配置就可以模拟网络请求。只需定义一个自定义的 Route 来为浏览器上下文模拟网络。

example.spec.ts
import { test, expect } from '@playwright/test';

test.beforeEach(async ({ context }) => {
// 为这个文件中的每个测试阻止所有 css 请求
await context.route(/.css$/, route => route.abort());
});

test('加载不带 css 的页面', async ({ page }) => {
await page.goto('https://playwright.dev');
// ... 测试代码
});

或者,你也可以使用 page.route() 来模拟单个页面的网络。

example.spec.ts
import { test, expect } from '@playwright/test';

test('加载不带图片的页面', async ({ page }) => {
// 阻止 png 和 jpeg 图片
await page.route(/(png|jpeg)$/, route => route.abort());

await page.goto('https://playwright.dev');
// ... 测试代码
});

HTTP 认证

执行 HTTP 认证。

playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
httpCredentials: {
username: 'bill',
password: 'pa55w0rd',
}
}
});

HTTP 代理

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

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

以下是全局代理的示例:

playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
proxy: {
server: 'http://myproxy.com:3128',
username: 'usr',
password: 'pwd'
}
}
});

也可以按上下文单独指定:

example.spec.ts
import { test, expect } from '@playwright/test';

test('应该在新上下文中使用自定义代理', async ({ browser }) => {
const context = await browser.newContext({
proxy: {
server: 'http://myproxy.com:3128',
}
});
const page = await context.newPage();

await context.close();
});

网络事件

您可以监控所有的Request请求和Response响应:

// 订阅'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');

或者使用page.waitForResponse()等待按钮点击后的网络响应:

// 使用通配符URL模式。注意这里不需要await
const responsePromise = page.waitForResponse('**/api/fetch_data');
await page.getByText('Update').click();
const response = await responsePromise;

变体

使用page.waitForResponse()等待Response响应

// 使用正则表达式。注意这里不需要await
const responsePromise = page.waitForResponse(/\.jpeg$/);
await page.getByText('Update').click();
const response = await responsePromise;

// 使用接收Response对象的谓词函数。注意这里不需要await
const responsePromise = page.waitForResponse(response => response.url().includes(token));
await page.getByText('Update').click();
const response = await responsePromise;

处理请求

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

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

变体

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

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

修改请求

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

// 将所有请求改为 POST 方法
await page.route('**/*', route => route.continue({ method: 'POST' }));

你可以通过修改后的参数继续请求。上面的示例移除了传出请求中的 HTTP 头信息。

中止请求

你可以使用 page.route()route.abort() 来中止请求。

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()。你可以通过选项覆盖响应中的特定字段:

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

全局 URL 模式匹配

Playwright 在网络拦截方法(如 page.route()page.waitForResponse())中使用简化的 glob 模式进行 URL 匹配。这些模式支持以下基本通配符:

  1. 星号:
    • 单个 * 匹配除 / 外的任意字符
    • 双星号 ** 匹配包括 / 在内的任意字符
  2. 问号 ? 仅匹配问号 ?。如需匹配任意字符,请改用 *
  3. 花括号 {} 可用于匹配逗号 , 分隔的选项列表
  4. 反斜杠 \ 可用于转义任何特殊字符(注意反斜杠本身需转义为 \\

示例:

  • https://example.com/*.js 匹配 https://example.com/file.js 但不匹配 https://example.com/path/file.js
  • https://example.com/?page=1 匹配 https://example.com/?page=1 但不匹配 https://example.com
  • **/*.js 同时匹配 https://example.com/file.jshttps://example.com/path/file.js
  • **/*.{png,jpg,jpeg} 匹配所有图片请求

重要说明:

  • glob 模式必须匹配整个 URL,而非仅匹配部分
  • 使用 glob 进行 URL 匹配时,需考虑完整的 URL 结构,包括协议和路径分隔符
  • 如需更复杂的匹配需求,建议改用 RegExp 而非 glob 模式

WebSockets

Playwright 原生支持 WebSockets 的检查、模拟和修改功能。请参阅我们的 API 模拟指南 了解如何模拟 WebSockets。

每当创建 WebSocket 时,都会触发 page.on('websocket') 事件。该事件包含 WebSocket 实例,可用于进一步检查 WebSocket 帧:

page.on('websocket', ws => {
console.log(`WebSocket 已打开: ${ws.url()}>`);
ws.on('framesent', event => console.log(event.payload));
ws.on('framereceived', event => console.log(event.payload));
ws.on('close', () => console.log('WebSocket 已关闭'));
});

缺失的网络事件与服务工作者(Service Workers)

Playwright 内置的 browserContext.route()page.route() 允许您的测试原生地路由请求并执行模拟和拦截操作。

  1. 如果您正在使用 Playwright 原生的 browserContext.route()page.route(),但发现网络事件缺失,可以通过将 serviceWorkers 设置为 'block' 来禁用 Service Workers。

  2. 可能是您正在使用 Mock Service Worker (MSW) 等模拟工具。虽然该工具开箱即用可以模拟响应,但它会添加自己的 Service Worker 来接管网络请求,从而使这些请求对 browserContext.route()page.route() 不可见。如果您同时需要网络测试和模拟功能,建议使用内置的 browserContext.route()page.route() 进行响应模拟

  3. 如果您不仅想使用 Service Workers 进行测试和网络模拟,还想路由和监听由 Service Workers 自身发出的请求,请参阅这个实验性功能