Skip to content

介绍网络监听、路由、代理、HTTP 认证、请求和响应修改。

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

查看我们的 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('loads page without css', async ({ page }) => {
await page.goto('https://playwright.dev');
// ... 测试写在这里
});

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

example.spec.ts

import { test, expect } from '@playwright/test';
test('loads page without images', async ({ page }) => {
// 阻止 png 和 jpeg 图片。
await page.route(/(png|jpeg)$/, route => route.abort());
await page.goto('https://playwright.dev');
// ... 测试写在这里
});

执行 HTTP 身份验证。

playwright.config.ts

import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
httpCredentials: {
username: 'bill',
password: 'pa55w0rd',
}
}
});
const context = await browser.newContext({
httpCredentials: {
username: 'bill',
password: 'pa55w0rd',
},
});
const page = await context.newPage();
await page.goto('https://example.com');

你可以配置页面通过 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'
}
}
});
const browser = await chromium.launch({
proxy: {
server: 'http://myproxy.com:3128',
username: 'usr',
password: 'pwd'
}
});

也可以按上下文指定:

example.spec.ts

import { test, expect } from '@playwright/test';
test('should use custom proxy on a new context', async ({ browser }) => {
const context = await browser.newContext({
proxy: {
server: 'http://myproxy.com:3128',
}
});
const page = await context.newPage();
await context.close();
});
const browser = await chromium.launch();
const context = await browser.newContext({
proxy: { server: 'http://myproxy.com:3128' }
});

你可以监控所有 RequestResponse

// 订阅 '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() 等待网络响应:

// 使用 glob URL 模式。注意没有 await。
const responsePromise = page.waitForResponse('**/api/fetch_data');
await page.getByText('Update').click();
const response = await responsePromise;

使用 page.waitForResponse() 等待 Response

// 使用 RegExp。注意没有 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();
// 给 title 添加前缀。
let body = await response.text();
body = body.replace('<title>', '<title>My prefix:');
await route.fulfill({
// 传递响应中的所有字段。
response,
// 覆盖响应正文。
body,
// 强制内容类型为 html。
headers: {
...response.headers(),
'content-type': 'text/html'
}
});
});

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 模式。

Playwright 开箱即支持 WebSockets 检查、模拟和修改。请参阅我们的 API 模拟指南,了解如何模拟 WebSockets。

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

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'));
});

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

如果你正在使用 Playwright 原生的 browserContext.route()page.route(),并且看起来缺失了网络事件,请通过将 serviceWorkers 设置为 'block' 来禁用 Service Workers。

可能是你正在使用 Mock Service Worker(MSW)这样的模拟工具。虽然此工具可以开箱即用地模拟响应,但它会添加自己的 Service Worker 来接管网络请求,因此会使这些请求对 browserContext.route()page.route() 不可见。如果你同时对网络测试和模拟感兴趣,请考虑使用内置的 browserContext.route()page.route() 进行响应模拟。

如果你感兴趣的不只是使用 Service Workers 进行测试和网络模拟,而是路由和监听 Service Workers 自身发出的请求,请参阅 本指南

-
0:000:00