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

如何拦截应用全场景Fetch请求且兼容File URL?

解决方案:全局拦截所有Fetch请求(含Web Worker)并适配File URL

你的需求确实有点棘手——既要覆盖主线程、所有模块/第三方库的Fetch,还要支持file://协议(Electron和Android WebView场景),之前的几个方案各自有局限性:重写window.fetch碰不到Worker,Service Worker不支持本地文件,提前启动Worker又没法影响其他Worker的作用域。

核心思路:重写Worker构造函数,自动注入Fetch拦截逻辑

每个Worker都有独立的WorkerGlobalScope,所以要拦截所有Worker的Fetch,必须在每个Worker启动时自动注入重写代码。通过替换全局的Worker构造函数,我们可以拦截所有新建Worker的请求,不管是你自己写的还是第三方库创建的。

完整实现代码

第一步:重写主线程的Fetch

先搞定主线程的所有Fetch请求:

// 保存原生Fetch
const originalWindowFetch = window.fetch;

// 重写主线程Fetch
window.fetch = function(input, init) {
  // 这里添加你的拦截逻辑,比如打印请求、修改参数等
  console.log('[主线程] Fetch被拦截:', input);
  
  // 调用原生Fetch
  return originalWindowFetch.apply(this, arguments);
};

第二步:重写Worker构造函数,自动注入拦截代码

这是关键部分,确保每个新建的Worker都会加载我们的Fetch重写逻辑:

// 保存原生Worker构造函数
const OriginalWorker = window.Worker;

// 替换全局Worker构造函数
window.Worker = function(scriptURL, options) {
  // 定义要注入到Worker中的Fetch重写代码
  const fetchOverrideCode = `
    // 保存Worker原生Fetch
    const originalWorkerFetch = self.fetch;
    
    // 重写Worker的Fetch
    self.fetch = function(input, init) {
      console.log('[Worker] Fetch被拦截:', input);
      // 这里添加你的拦截处理逻辑
      return originalWorkerFetch.apply(this, arguments);
    };
  `;

  // 构造包装脚本:先执行拦截代码,再加载原Worker脚本
  let wrapperScript;
  if (typeof scriptURL === 'string') {
    // 处理普通脚本URL(包括file://协议)
    wrapperScript = `
      ${fetchOverrideCode}
      // 加载原Worker脚本
      importScripts('${scriptURL}');
    `;
  } else if (scriptURL instanceof Blob) {
    // 处理Blob形式的Worker脚本
    const blobUrl = URL.createObjectURL(scriptURL);
    wrapperScript = `
      ${fetchOverrideCode}
      importScripts('${blobUrl}');
      // 清理临时Blob URL
      URL.revokeObjectURL('${blobUrl}');
    `;
  }

  // 将包装脚本转为Blob URL
  const wrapperBlob = new Blob([wrapperScript], { type: 'application/javascript' });
  const wrapperBlobUrl = URL.createObjectURL(wrapperBlob);

  // 创建原生Worker实例
  const worker = new OriginalWorker(wrapperBlobUrl, options);

  // 监听Worker生命周期,清理Blob URL避免内存泄漏
  const cleanup = () => URL.revokeObjectURL(wrapperBlobUrl);
  worker.addEventListener('error', cleanup);
  worker.addEventListener('messageerror', cleanup);
  worker.addEventListener('terminate', cleanup);

  return worker;
};

第三步:(可选)处理SharedWorker

如果你的应用用到了SharedWorker,用同样的逻辑重写即可:

const OriginalSharedWorker = window.SharedWorker;
window.SharedWorker = function(scriptURL, options) {
  // 复用上面的fetchOverrideCode和wrapperScript逻辑
  const fetchOverrideCode = `
    const originalWorkerFetch = self.fetch;
    self.fetch = function(input, init) {
      console.log('[SharedWorker] Fetch被拦截:', input);
      return originalWorkerFetch.apply(this, arguments);
    };
  `;

  let wrapperScript;
  if (typeof scriptURL === 'string') {
    wrapperScript = `
      ${fetchOverrideCode}
      importScripts('${scriptURL}');
    `;
  } else if (scriptURL instanceof Blob) {
    const blobUrl = URL.createObjectURL(scriptURL);
    wrapperScript = `
      ${fetchOverrideCode}
      importScripts('${blobUrl}');
      URL.revokeObjectURL('${blobUrl}');
    `;
  }

  const wrapperBlob = new Blob([wrapperScript], { type: 'application/javascript' });
  const wrapperBlobUrl = URL.createObjectURL(wrapperBlob);
  const sharedWorker = new OriginalSharedWorker(wrapperBlobUrl, options);
  
  const cleanup = () => URL.revokeObjectURL(wrapperBlobUrl);
  sharedWorker.port.addEventListener('error', cleanup);
  sharedWorker.port.addEventListener('messageerror', cleanup);

  return sharedWorker;
};

关键注意事项

  1. 执行顺序优先:这段重写代码必须在所有其他脚本(包括第三方库)加载之前执行,比如放在HTML的<head>最顶部,或者在Electron的preload脚本中运行(preload脚本会在渲染进程加载前执行)。
  2. File URL适配:因为整个逻辑都是基于本地脚本注入,不需要Service Worker,所以完全支持file://协议,不管是Electron还是Android WebView都能正常工作。
  3. 第三方库兼容性:只要第三方库是通过标准new Worker()方式创建Worker,都会被拦截到——不需要修改任何第三方库的代码。
  4. 内存泄漏防范:记得在Worker的错误、终止等事件中清理创建的Blob URL,避免内存占用过高。

为什么之前的方案不行?

  • 直接重写window.fetch:只能覆盖主线程的Fetch,Worker有独立的全局作用域,根本看不到主线程的window对象。
  • Service Worker:本身就不支持file://协议(浏览器安全限制),而且需要HTTPS或localhost环境,不符合你的需求。
  • 提前启动自定义Worker:每个Worker的作用域是完全隔离的,你创建的Worker只能修改自己的self.fetch,无法影响其他Worker。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:13:45