You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Playwright中拦截非导航类HTTP请求?

问题:Playwright中仅页面导航请求被拦截,如何拦截所有请求并统一添加Test ID?

你写的测试代码里,只有最后一行page.goto的请求被context.route拦截,前面的几种请求都没触发拦截逻辑:

import {test} from "@playwright/test";

test('experimental test', async ({ page, context }) => {
    await context.route('**/*', (route) => {
        console.log('Intercepted:', route.request().url());
        route.continue();
    });

    await fetch('https://example.com'); // 未被拦截
    await context.request.get('https://example.com'); // 未被拦截
    await context.request.fetch('https://example.com'); // 未被拦截
    await page.request.get('https://example.com'); // 未被拦截
    await page.goto('https://example.com'); // 仅这个被拦截
});

你的核心需求是:给测试中所有请求(包括初始化用的fetch请求)自动添加唯一的Test ID header,让每个测试能使用独立的数据库实例和种子数据,示例的拦截逻辑如下:

base.beforeEach(async ({context}, testInfo) => {
    await context.route('**/*', async route => {
        const headers = route.request().headers();
        headers['Playwright-Test-Id'] = testInfo.project.name + '-' + testInfo.titlePath.join('-');
        await route.continue({ headers });
    });
})

解决方案

1. 先搞懂不同请求的运行环境

Playwright的context.route只能拦截浏览器页面上下文中的请求(比如page.goto、页面内的fetch/XHR),而你代码里的其他请求分属不同环境:

  • await fetch(...):运行在测试脚本的Node.js环境,和浏览器上下文完全无关
  • context.request/page.request:属于Playwright的API请求,默认不被context.route拦截

2. 拦截Playwright API请求(context.request/page.request)

用context.routeFromAPI()方法专门处理这类API层面的请求,和context.route配合就能覆盖页面+API请求:

base.beforeEach(async ({context}, testInfo) => {
    const testId = `${testInfo.project.name}-${testInfo.titlePath.join('-')}`;

    // 拦截浏览器页面上下文的请求
    await context.route('**/*', async route => {
        const headers = {...route.request().headers(), 'Playwright-Test-Id': testId};
        await route.continue({ headers });
    });

    // 拦截context.request/page.request发起的API请求
    await context.routeFromAPI('**/*', async route => {
        const headers = {...route.request().headers(), 'Playwright-Test-Id': testId};
        await route.continue({ headers });
    });
})

3. 处理Node.js环境的fetch请求

如果必须用Node的fetch发初始化请求,需要在Node层重写fetch方法,统一添加header:

base.beforeEach(async ({context}, testInfo) => {
    const testId = `${testInfo.project.name}-${testInfo.titlePath.join('-')}`;
    // 保存原生fetch方法
    const originalFetch = global.fetch;

    // 重写全局fetch,自动注入Test ID
    global.fetch = async (url, options = {}) => {
        options.headers = {...options.headers, 'Playwright-Test-Id': testId};
        return originalFetch(url, options);
    };

    // 测试结束后恢复原生fetch,避免影响其他测试
    testInfo.attachments.push({
        name: 'restore-fetch',
        body: '',
        contentType: 'text/plain',
        onDispose: () => {
            global.fetch = originalFetch;
        }
    });

    // 配合前面的页面请求和API请求拦截逻辑
    await context.route('**/*', async route => {
        const headers = {...route.request().headers(), 'Playwright-Test-Id': testId};
        await route.continue({ headers });
    });

    await context.routeFromAPI('**/*', async route => {
        const headers = {...route.request().headers(), 'Playwright-Test-Id': testId};
        await route.continue({ headers });
    });
})

4. 更简洁的替代方案(推荐)

如果不需要用Node的fetch,可以把所有初始化请求都改用context.request发起,这样只需要routeFromAPI+route就能覆盖所有请求,不用处理Node层的fetch重写,代码更清爽。


内容的提问来源于stack exchange,提问作者kmc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 19:23:23