修改请求头插件迁移manifest V3记录

迁移之前我们先来看看在manifest v3中,如何处置webRequestBlocking权限的吧。

理清v3 webRequestBlocking

我们知道在manifest v2中拦截,修改,取消请求需要使用 asyncBlocking,blocking 选项的,使用这些选项的前提又必须设置webRequestBlocking权限。其他选项(requestHeadersresponseHeadersextraHeaders)都是不需要的,所以在manifest v3中依旧可以直接使用来获取请求的相关内容。

至此,我们可以知道manifest v3并没有对webRequest做限制,只是限制了webRequestBlocking权限,所以webRequest权限相关api可以正常使用。

配置webRequestBlocking权限相关选项(asyncBlocking,blocking)回调是不会执行的,并且会抛出错误。如果未配置asyncBlocking,blocking选项,即使webRequest相关API回调设置了返回值也会被忽略。

js 复制代码
console.log("manifest v3");
chrome.webRequest.onBeforeRequest.addListener(
  (details) => {
    console.log("这里不会回调", details)
    return { cancel: true }; // 未配置`asyncBlocking`,`blocking`选项,即使`webRequest`相关API回调设置了返回值也会被忽略
  },
  { urls: ["<all_urls>"] },
  ["blocking"] // 配置webRequestBlocking权限相关选项回调是不会执行的,并且会抛出错误。
);

并且加载到扩展管理中可以直接查看错误来源

插件迁移

manifest.json

  • manifest_version 更新版本
js 复制代码
"manifest_version": 2,
  
=>
  
"manifest_version": 3,
  • background
js 复制代码
 "background": {
    "scripts": ["background.js"],
    "persistent": true
  },
  
  =>
  
  "background": {
    "service_worker": "background.js"
  }
  • permissions, v2中主机权限和api权限都在一起,v3进行的分离
js 复制代码
"permissions": [
    "tabs",
    "storage",
    "webRequest",
    "webRequestBlocking",
    "http://*/*",
    "https://*/*"
],

=>

"permissions": [
    "tabs",
    "storage",
    "declarativeNetRequest"
],
"host_permissions": [
    "http://*/*",
    "https://*/*"
],
  • 统一action,browser_action 和 page_action 在 MV3 中被统一为 action API
js 复制代码
"browser_action": {
  "default_title": "添加/修改HTTP请求头",
  "default_icon": {
    "16": "./icons/cloud16_off.png",
    "32": "./icons/cloud32_off.png",
    "48": "./icons/cloud48_off.png",
    "128": "./icons/cloud128_off.png"
  },
  "default_popup": "popup.html"
},

=>

"action": {
    "default_title": "添加/修改HTTP请求头",
    "default_icon": {
      "16": "./icons/cloud16_off.png",
      "32": "./icons/cloud32_off.png",
      "48": "./icons/cloud48_off.png",
      "128": "./icons/cloud128_off.png"
    },
    "default_popup": "popup.html"
},

api迁移

整体介绍

由于在manifest v3中废弃了webRequestBlocking权限,所以做请求修改就必须使用新增的declarativeNetRequest权限。

chrome.declarativeNetRequest API 用于通过指定声明性规则来屏蔽或修改网络请求。但是又提供了相关getDynamicRules, updateDynamicRules等API让其可以编程式的修改请求,而不仅仅通过静态配置表。

不管是静态规则表,还是动态规则都是通过设置规则集来进行修改网络请求的,所以我们先来看看规则集如何编写。

规则由四部分组成

js 复制代码
interface Rule {
  id: number;                    // 唯一ID (1-999999)
  priority?: number;            // 优先级 (1-999999,默认1)
  condition: RuleCondition;     // 匹配条件
  action: RuleAction;           // 执行动作
}
  • priority 如果同时匹配到多个规则,那么将会匹配到priority最大的。如果priority相同,按操作排序(allowallowAllRequests > block > upgradeScheme > redirect)。如果有多个扩展程序规则匹配同一网址,并且规则属于同一类型,Chrome 会选择来自最近安装的扩展程序的规则。
  • condition
js 复制代码
interface RuleCondition {
  // ========== URL 过滤(二选一)相关 ==========
  urlFilter?: string;           // 简单URL过滤 可通过isUrlFilterCaseSensitive设置是否区分大小写,默认不区分,为false
  模式匹配令牌介绍
    - * :通配符:匹配任意数量的字符。
    - | :左/右锚点:如果用于模式的任一端,则分别指定网址的开头/结尾。
    - || :域名锚点:如果用于模式的开头,则指定网址的(子)网域的开头。
    - ^ :分隔符字符:匹配除字母、数字或以下字符以外的任何内容:`_`、`-`、`.` 或 `%`。这也匹配网址的末尾。
  // 每条规则在编译后的大小必须小于 2KB
  regexFilter?: string;         // 正则表达式过滤 可通过isUrlFilterCaseSensitive设置是否区分大小写,默认不区分,为false
  isUrlFilterCaseSensitive?: boolean;         // URL过滤器是否区分大小写
  
  // ========== 域名匹配相关 ==========
  domains?: string[];           // 匹配的域名 Chrome 101弃用
  excludedDomains?: string[];   // 排除的域名 Chrome 101弃用
  initiatorDomains?: string[];                // 匹配发起请求的页面域名
  excludedInitiatorDomains?: string[];        // 排除发起请求的页面域名
  
  // ========== 资源类型相关 ==========
  resourceTypes?: ResourceType[];             // 匹配资源类型
  excludedResourceTypes?: ResourceType[];     // 排除资源类型

  // ========== 域名类型相关 ==========
  // 发起方和接收方属于同域名
  domainType?: "firstParty" | "thirdParty";   // 第一方或第三方

  // ========== 请求方法相关 ==========
  requestMethods?: RequestMethod[];           // 匹配请求方法
  excludedRequestMethods?: RequestMethod[];   // 排除请求方法

  // ========== 标签页相关 ==========
  tabIds ?: number[];                          // 匹配标签页ID
  excludedTabIds ?: number[];                  // 排除标签页ID

  // ========== 响应头相关 ==========
  responseHeaders ?: HeaderInfo[];           // 匹配响应头
  excludedResponseHeaders ?: HeaderInfo[];   // 排除响应头

  // ========== 请求头相关 ==========
  requestHeaders ?: HeaderInfo[];            // 匹配请求头
  excludedRequestHeaders ?: HeaderInfo[];    // 排除请求头
}

// 资源类型枚举
type ResourceType = 
  | "main_frame"      // 主框架
  | "sub_frame"       // 子框架(iframe)
  | "stylesheet"      // CSS样式表
  | "script"          // JavaScript脚本
  | "image"           // 图片
  | "font"            // 字体
  | "object"          // 对象(插件)
  | "xmlhttprequest"  // XMLHttpRequest
  | "ping"            // Beacon/ping请求
  | "csp_report"      // CSP报告
  | "media"           // 媒体资源
  | "websocket"       // WebSocket连接
  | "other";          // 其他类型

// 请求方法枚举
RequestMethod {
    CONNECT = "connect",
    DELETE = "delete",
    GET = "get",
    HEAD = "head",
    OPTIONS = "options",
    PATCH = "patch",
    POST = "post",
    PUT = "put",
    OTHER = "other",
}

请求头
interface HeaderInfo {
    excludedValues?: string[];
    // 头部的名称。只有在未指定 `values` 和 `excludedValues` 时,此条件才会仅根据名称进行匹配。
    header: string;
    // 如果指定了此条件,则当标头的值与此列表中的至少一个模式匹配时,此条件即为匹配。此功能支持不区分大小写的标头值匹配以及以下构造:
    *:匹配任意数量的字符。
    ?:匹配零个或一个字符。
    values?: string[];
}
js 复制代码
// 安全规则是指操作为 `block`、`allow`、`allowAllRequests` 或 `upgradeScheme` 的规则
type RuleAction = 
  | BlockAction
  | RedirectAction
  | AllowAction
  | UpgradeSchemeAction
  | ModifyHeadersAction
  | AllowAllRequestsAction;
  
interface BlockAction {
  type: "block";
}

interface ModifyHeadersAction {
  type: "modifyHeaders";
  requestHeaders?: HeaderOperation[];
  responseHeaders?: HeaderOperation[];
}

interface HeaderOperation {
  header: string;
  // append子对特定头字段修改  https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest?hl=zh-cn#header_modification
  operation: "set" | "remove" | "append";
  value?: string;
}

interface RedirectAction {
  type: "redirect";
  redirect: {
    // 重定向到扩展资源
    extensionPath?: string;
    
    // 重定向到URL
    url?: string;
    
    // 正则替换重定向
    regexSubstitution?: string;
    
    // 转换重定向
    transform?: {
      scheme?: "http" | "https";
      host?: string;
      port?: string;
      path?: string;
      query?: string;
      fragment?: string;
      username?: string;
      password?: string;
    };
  };
}

interface AllowAction {
  type: "allow";
}

interface UpgradeSchemeAction {
  type: "upgradeScheme";
}

interface AllowAllRequestsAction {
  type: "allowAllRequests";
}

规则集类型

注意:不同类型的规则集的规则ID是可以重复的,因为有规则集ID作为区分,静态自己设置,动态规则集ID为_dynamic,会话规则集ID为_session

动态

(version >= 121 安全规则MAX_NUMBER_OF_DYNAMIC_RULES最多30000条。MAX_NUMBER_OF_UNSAFE_DYNAMIC_RULES非安全和安全最多5000条(即非安全规则最多5000条) version <= 120 动态,会话加起来最多5000)

可在浏览器会话和扩展程序升级期间保持不变,并且在扩展程序使用期间通过 JavaScript 进行管理。

getDynamicRules

获取动态规则

js 复制代码
chrome.declarativeNetRequest.getDynamicRules(
  {
    ruleIds?: [规则ID],
  },
  (res) => {
    console.log("获取动态规则集", res);
  }
);
updateDynamicRules
js 复制代码
// 更新动态规则
chrome.declarativeNetRequest.updateDynamicRules({
  addRules: [ /* 规则数组 */ ],
  removeRuleIds: [ /* 要移除的规则ID数组 */ ]
}, callback);

会话

(version >= 121 MAX_NUMBER_OF_SESSION_RULES最多5000条。 version <= 120 动态,会话加起来最多5000)

在浏览器关闭时以及安装新版本的扩展程序时清除。在使用扩展程序时,会话规则通过 JavaScript 进行管理。

getSessionRules
js 复制代码
// 获取会话规则
chrome.declarativeNetRequest.getSessionRules(
    {
      ruleIds?: [ /* 规则ID数组 */]
    },
    callback
);
updateSessionRules
js 复制代码
// 更新会话规则
chrome.declarativeNetRequest.updateSessionRules({
  addRules: [ /* 规则数组 */ ],
  removeRuleIds: [ /* 要移除的规则ID数组 */ ]
});

静态

MAX_NUMBER_OF_ENABLED_STATIC_RULESETS 一次最多启用50个规则集(最多规则集为MAX_​NUMBER_​OF_​STATIC_​RULESETS100个),GUARANTEED_MINIMUM_STATIC_RULES最多30000条规则。在 Chrome 120 之前,扩展程序最多只能有 50 个静态规则集,并且一次只能启用其中的 10 个)

在安装或升级扩展程序时进行打包、安装和更新。静态规则存储在 JSON 格式的规则文件中,并在清单文件中列出。

js 复制代码
{
  ...
  "declarative_net_request" : {
    "rule_resources" : [{
      "id": "ruleset_1",
      "enabled": true,
      "path": "rules_1.json" // 无需配置可访问文件
    },
    ]
  }
}
getAvailableStaticRuleCount

开发文档中说上限为30000,但是测试发现可以是330000个。

获取还可以添加多少静态规则个数。

getDisabledRuleIds

获取指定规则集已停用的规则

js 复制代码
chrome.declarativeNetRequest.getDisabledRuleIds(
  { rulesetId: "静态规则集ID" },
  (res) => {
    console.log("获取停用的静态规则", res);
  }
);


// 停用1,2
chrome.declarativeNetRequest.updateStaticRules({
  disableRuleIds: [1, 2],
  // enableRuleIds: [需要启用的规则ID],
  rulesetId: "ruleset_1",
});

chrome.declarativeNetRequest.getDisabledRuleIds(
  { rulesetId: "ruleset_1" },
  (res) => {
    console.log("获取指定静态规则集已停用的规则", res);
  }
);
getEnabledRulesets

获取当前开启的静态规则集

js 复制代码
chrome.declarativeNetRequest.getEnabledRulesets((res) => {
  console.log("当前开启的静态规则集id", res);
});
updateEnabledRulesets

更新静态规则集

js 复制代码
chrome.declarativeNetRequest.updateEnabledRulesets(
  {
    disableRulesetIds: [需要禁用的规则集ID],
    enableRulesetIds: [需要启用的规则集ID],
  }
);
updateStaticRules

更新指定静态规则集中的规则

js 复制代码
chrome.declarativeNetRequest.updateStaticRules(
  {
    disableRuleIds: [需要禁用的规则ID],
    enableRuleIds: [需要启用的规则ID],
    rulesetId: [静态规则集ID], // 必须
  }
);
setExtensionActionOptions

在扩展图标上显示匹配的规则数

js 复制代码
// 在扩展图标上显示匹配的规则数
chrome.declarativeNetRequest.setExtensionActionOptions({
  displayActionCountAsBadgeText: true
});

测试API

isRegexSupported

检查给定的正则表达式是否会作为 regexFilter 规则条件受到支持

js 复制代码
const regexOptions = {
  regex: "^https?://([^/]+\\.)?example\\.com/.*",
  isCaseSensitive: false, // 指定的 `regex` 是否区分大小写。默认值为 true
  requireCapturing: true // 指定的 `regex` 是否需要捕获。只有指定了 `regexSubstition` 操作的重定向规则才需要捕获。默认值为 false
};

chrome.declarativeNetRequest.isRegexSupported(regexOptions, (result) => {
  if (result.isSupported) {
    console.log("正则表达式受支持");
  } else {
    console.log("正则表达式可能过于复杂或不受支持");
  }
});
testMatchOutcome

检查扩展程序的任何 declarativeNetRequest 规则是否会与假设的请求匹配。注意:此方法仅适用于未打包的扩展程序,因为此方法仅用于扩展程序开发期间。

js 复制代码
interface TestMatchRequestDetails {
    /** 请求发起的url. */
    initiator?: string;
    /** http请求方法,默认为get */
    method?: `${RequestMethod}`;
    /**
     * 响应头对象
     * @since Chrome 129
     */
    responseHeaders?: { [name: string]: unknown };
    /** 请求发生的标签页的 ID。无需与实际标签页 ID 对应。默认值为 -1,表示请求与任何标签页无关。*/
    tabId?: number;
    /** 请求资源类型 */
    type: `${ResourceType}`;
    /** 请求的url. */
    url: string;
}
js 复制代码
chrome.declarativeNetRequest.testMatchOutcome(
  {
    url: "https://www.baidu.com",
    type: "main_frame",
  },
  (res) => {
    console.log("测试匹配结果", res);
  }
);
getMatchedRules

返回与扩展程序匹配的所有规则。

js 复制代码
interface MatchedRulesFilter {
  tabId?: number;     // 可选:限制特定标签页
  timestamp?: number; // 可选:限制时间戳之后
}

interface RulesMatchedInfo {
  rule: {
    ruleId: number;      // 规则ID
    rulesetId?: string;  // 规则集ID(仅静态规则)
  };
  request: {
    url: string;         // 请求URL
    originUrl?: string;  // 原始URL(重定向前)
    method: string;      // HTTP方法
    tabId: number;       // 标签页ID
    type: ResourceType;  // 资源类型
    frameId: number;     // 框架ID
    documentId?: string; // 文档ID
    documentLifecycle?: string; // 文档生命周期
    frameType?: string;  // 框架类型
    parentDocumentId?: string; // 父文档ID
    parentFrameId: number; // 父框架ID
    requestId: string;   // 请求ID
  };
  timeStamp: number;    // 匹配时间戳
}

chrome.declarativeNetRequest.getMatchedRules(
  filter?: MatchedRulesFilter,
  callback?: (details: { rulesMatchedInfo: RulesMatchedInfo[] }) => void
): Promise<{ rulesMatchedInfo: RulesMatchedInfo[] }>;
onRuleMatchedDebug

匹配到具体规则时触发。

js 复制代码
chrome.declarativeNetRequest.onRuleMatchedDebug.addListener((matchItem) => {
  console.log("匹配到", matchItem);
});

参考

相关推荐
_默_6 小时前
前端常用依赖归纳【vueuse\lodash-es\dayjs\bignumber】
大数据·前端·elasticsearch
cindershade6 小时前
使用 SSE 单向推送实现 系统通知功能
前端
Mapmost6 小时前
【高斯泼溅】Mapmost分区训练,让大场景3DGS建模从此高效且高质
前端
进击的野人6 小时前
Vue生命周期详解:从创建到销毁的全过程
前端·vue.js·面试
鹏北海6 小时前
微前端实现方式:HTML Entry 与 JS Entry 的区别
前端·javascript·面试
用户4099322502126 小时前
Vue3的`:style`对象语法:单位、属性名、响应式,这些细节你都踩过坑吗?
前端·ai编程·trae
Mintopia6 小时前
🎯 Rect 中鼠标移动拾取元素可行性架构分析
前端·react.js·架构
水臭6 小时前
一个“够用就好”的浏览器端实时预览编辑器
前端
coding随想6 小时前
前端革命:自定义元素如何让HTML元素“活“起来,重构你的开发体验!
前端·重构·html