前言
前面我们介绍了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 编码)
});