测试开发之路之chrome插件(五)修改网络请求

前言

前面我们介绍了Chrome插件中的界面组件,本期我们讲一下如何使用插件修改网络请求。
在测试的过程中,会经常用到修改请求头、重定向请求地址以及修改返回内容。本章节主要讲这三个部分,其余部分测试很少或者几乎不会用到,暂时不在这儿赘述。

1. 修改请求头

我们测试过程中常常会遇到切换预发布环境和生产环境的情况。走网关去分流固然是一个方法,但我们也可以通过修改请求头达到切换环境的目的。
修改请求头需要用到 chrome.declarativeNetRequest API,官方文档:👉点击这里
在 Chrome Manifest V3 中,declarativeNetRequest API 是用于拦截和修改网络请求的推荐方式,它可以代替 webRequestBlocking 进行声明式请求修改。这个 API 主要用于执行简单的拦截、修改或重定向操作,不需要阻塞请求,可以提高性能并符合 Manifest V3 的安全要求。
具体实现步骤如下:

1.1 设置 manifest.json 文件

 "permissions": [
    "tabs",
    "storage",
    "contextMenus",
    "notifications",
    "declarativeNetRequest",
    "webRequest"
  ],
  "host_permissions": [
    "http://*/*",
    "https://*/*"
  ],
  //静态修改的时候,可以在这儿配置,本文主要介绍动态修改的方法,在这边可以不配置。
  "declarative_net_request": {
    "rule_resources": []
  }

declarative_net_request 这个配置项主要用于静态修改,本文暂不涉及,下面给出一个示例的配置,如果有静态修改的需求,可以按照下面的配置来。

"declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset1",
        "enabled": true,
        "rules": [
          {
            "id": 1,
            "condition": {
              "urlFilter": "example.com",
              "resourceTypes": ["main_frame"]
            },
            "action": {
              "type": "redirect",
              "redirect": {
                "url": "https://newexample.com"
              }
            }
          },
          {
            "id": 2,
            "condition": {
              "urlFilter": "example.com",
              "resourceTypes": ["image"]
            },
            "action": {
              "type": "block"
            }
          }
        ]
      }
    ]
  }
}

1.2 编写service-worker.js文件

chrome.runtime.onInstalled.addListener(() => {
    chrome.declarativeNetRequest.getDynamicRules({}, (rules) => {
        const oldRuleIds = rules.map(rule => rule.id);
        //更改当前规则
        chrome.declarativeNetRequest.updateDynamicRules({
            removeRuleIds: oldRuleIds,
            addRules: [
                {
                    id: 1,
                    //https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest#type-RuleCondition
                    condition: {
                        urlFilter: "*",
                        //https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest#type-ResourceType
                        resourceTypes: ["main_frame","sub_frame","xmlhttprequest"]
                    },
                    action: {
                    	//https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest#type-RuleActionType
                        type: "modifyHeaders",
                        //https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest#type-RuleAction
                        requestHeaders: [
                            {
                                header: "Accept-Language",
                                //https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest#type-HeaderOperation
                                operation: "set",
                                value: "MyValue"
                            },
                            {
                                header: "X-Canary-Staging",
                                operation: "set",
                                value: "staging"
                            }
                        ]
                    }
                }
            ]
        });
        //查看当前规则
        chrome.declarativeNetRequest.getDynamicRules({}, (rules) => {
            if (chrome.runtime.lastError) {
                console.error("Error fetching rules:", chrome.runtime.lastError);
            } else {
                console.log("Current dynamic rules:", rules);
            }
        });
    });
});

注意点:

  • 确保每个规则的 id 是唯一的,避免重复。
  • 确认规则格式是否正确。
  • 检查权限是否正确。

上面每一行代码的解释,都在代码上方有注释声明,具体解释可以进官方文档查询

2. 重定向请求地址

重定向我们还是用 chrome.declarativeNetRequest API。manifest.json文件修改同上,这边不做赘述,直接上示例代码。

chrome.runtime.onInstalled.addListener(() => {
    chrome.declarativeNetRequest.getDynamicRules({}, (rules) => {
        const oldRuleIds = rules.map(rule => rule.id);
        chrome.declarativeNetRequest.updateDynamicRules({
            removeRuleIds: oldRuleIds,
            addRules: [
                {
                    id: 1,
                    condition: {
                        urlFilter: "https://msg.youkuaiyun.com/v1/web/message/view/unread",
                        resourceTypes: ["main_frame","sub_frame","xmlhttprequest"]
                    },
                    action: {
                        type: "redirect",
                        redirect:  { url: "https://run.mocky.io/v3/f6a69750-1dbe-424f-aeed-2679c2ee1410" }
                    }
                }
            ]
        });
        chrome.declarativeNetRequest.getDynamicRules({}, (rules) => {
            if (chrome.runtime.lastError) {
                console.error("Error fetching rules:", chrome.runtime.lastError);
            } else {
                console.log("Current dynamic rules:", rules);
            }
        });
    });
});

3. 修改返回数据

在 Chrome V3 版本的扩展中,您可以使用 chrome.debugger API 来修改接口响应的数据。官方文档:👉点击这里
这是通过拦截和修改网络请求来实现的。以下是如何使用 chrome.debugger 修改接口响应数据的基本步骤:

3.1 设置 manifest.json 文件

  "permissions": [
    "debugger"
  ],
  "host_permissions": ["<all_urls>"]

3.2 启用调试器

setTimeout(function() {
    chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
        if(tabs.length > 0) {
            const tabId=tabs[0].id;
            const debuggeeId={tabId}
            chrome.debugger.attach(debuggeeId,'1.3',()=>{
                chrome.debugger.sendCommand(debuggeeId, 'Fetch.enable', {
                    patterns: [{
                        urlPattern: '*', // 使用通配符先捕获所有请求,确认 POST 请求是否能被拦截
                        requestStage: 'Request'
                    }]
                }, () => {
                    console.log('Fetch domain enabled');
                });
            });
        }
    })
}, 3000);

3.3 监听并拦截请求

chrome.debugger.onEvent.addListener(function(debuggeeId, message, params) {
    //Fetch.requestPaused相关解释https://chromedevtools.github.io/devtools-protocol/
    if (message === 'Fetch.requestPaused') {
        if (params.request.method === "OPTIONS") {
            // 对预检请求,可以选择直接继续请求,不做修改
            chrome.debugger.sendCommand(debuggeeId, 'Fetch.continueRequest', {
                requestId: params.requestId,
                responseCode: 200,
                responseHeaders: [
                    { name: 'Access-Control-Allow-Origin', value: '*' },
                    { name: 'Access-Control-Allow-Methods', value: 'GET, POST, OPTIONS' },
                    { name: 'Access-Control-Allow-Headers', value: 'Content-Type' }
                ],
                body: btoa('')  // 空响应体
            });
            return;
        }
        if (params.request.url.includes("msg.youkuaiyun.com/v1/web/message/view/unread")) {
            console.log('捕获到请求:', params.request.url);
            console.log('请求:', params.request);
            // 假设你要修改响应体,首先构造新的响应体(例如 JSON 格式)
            const modifiedResponse = {
                newKey: 'newValue',
                originalKey: 'originalValue'
            };

            // 将 JSON 转换为字符串,并进行 Base64 编码
            const encodedBody = btoa(JSON.stringify(modifiedResponse));

            // 使用 Fetch.fulfillRequest 修改响应
            chrome.debugger.sendCommand(debuggeeId, 'Fetch.fulfillRequest', {
                requestId: params.requestId,
                responseCode: 200,
                responseHeaders: [
                    { name: 'Content-Type', value: 'application/json' },
                    { name: 'Access-Control-Allow-Origin', value: 'https://www.youkuaiyun.com' },
                    { name: 'Access-Control-Allow-Credentials', value: 'true' }
                ],
                body: encodedBody  // 修改后的响应体(Base64 编码)
            });
        } else {
            // 对其他请求继续处理
            chrome.debugger.sendCommand(debuggeeId, 'Fetch.continueRequest', {
                requestId: params.requestId
            });
        }
    }
});

3.4 可能遇到的问题

3.4.1 出现CORS错误

在这里插入图片描述
出现 CORS(跨域资源共享)错误,通常意味着浏览器认为返回的响应不满足安全策略要求。以下是几个可能的原因及解决方案:

3.4.1.1 修改响应头以允许跨域访问

当你拦截请求并使用 Fetch.fulfillRequest 返回自定义响应时,需要确保响应头中包含允许跨域访问的字段。例如,添加 Access-Control-Allow-Origin:

chrome.debugger.sendCommand(debuggeeId, 'Fetch.fulfillRequest', {
    requestId: params.requestId,
    responseCode: 200,
    responseHeaders: [
        { name: 'Content-Type', value: 'application/json' },
        { name: 'Access-Control-Allow-Origin', value: '*' }
    ],
    body: encodedBody
});
3.4.1.2 检查扩展权限

确保在扩展的 manifest.json 文件中配置了正确的权限,尤其是 host_permissions,以便你的扩展能够访问目标域。例如:

{
  "host_permissions": ["<all_urls>"],
  "permissions": [
    "debugger",
    "tabs"
  ]
}
3.4.1.3 注意预检请求(OPTIONS)

跨域请求通常会先发送一个预检(OPTIONS)请求。如果你的代码对 OPTIONS 请求没有做处理,可能会导致 CORS 错误。当前示例中对 OPTIONS 请求做了直接放行(Fetch.continueRequest),但需要确保预检响应也包含允许跨域的头信息。你可以考虑对 OPTIONS 请求返回适当的响应,例如:

if (params.request.method === "OPTIONS") {
    chrome.debugger.sendCommand(debuggeeId, 'Fetch.fulfillRequest', {
        requestId: params.requestId,
        responseCode: 200,
        responseHeaders: [
            { name: 'Access-Control-Allow-Origin', value: '*' },
            { name: 'Access-Control-Allow-Methods', value: 'GET, POST, OPTIONS' },
            { name: 'Access-Control-Allow-Headers', value: 'Content-Type' }
        ],
        body: btoa('')  // 空响应体
    });
    return;
}

3.4.2 The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute

在这里插入图片描述
这是因为请求使用了凭证模式(withCredentials 为 true),在这种情况下浏览器不允许响应头中使用通配符(*)作为 Access-Control-Allow-Origin 的值。
原因说明:

  • 凭证模式(withCredentials):当 XMLHttpRequest 或 fetch 请求设置了 withCredentials 属性,表示请求会携带 Cookie、HTTP 认证信息等敏感数据。
  • CORS 限制:在这种情况下,服务器响应的 Access-Control-Allow-Origin 必须明确指定请求的来源,而不能使用通配符 *。同时,还需要设置 Access-Control-Allow-Credentials 为 true。

解决方案
指定明确的来源,将响应头中的 Access-Control-Allow-Origin 改为请求实际来源。

chrome.debugger.sendCommand(debuggeeId, 'Fetch.fulfillRequest', {
    requestId: params.requestId,
    responseCode: 200,
    responseHeaders: [
        { name: 'Content-Type', value: 'application/json' },
        { name: 'Access-Control-Allow-Origin', value: 'https://www.youkuaiyun.com' },
        { name: 'Access-Control-Allow-Credentials', value: 'true' }
    ],
    body: encodedBody  // 修改后的响应体(Base64 编码)
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值