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

Chrome扩展拦截当前标签POST请求失败问题及方案咨询

问题描述

我们希望通过Chrome扩展读取/保存POST请求的请求体(payload):

  • 场景:目标网页会偶尔向服务器提交POST请求。
  • 需求:Chrome扩展的popup.html中有一个id为startIntercept的按钮,点击后需将当前标签页下一次触发的POST请求的所有详情(请求头、请求体)保存到会话变量中,无需关注响应内容。
  • 尝试方案:通过覆写window.XMLHttpRequest.prototype.open方法实现,保存原open方法后创建新方法拦截请求,但注入startWork.js脚本后仍无法拦截请求,现咨询失败原因及更优实现方案。

现有代码

let startIntercept = document.getElementById("startIntercept");

startIntercept.addEventListener("click", async () => {
    let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    chrome.scripting.executeScript({
        target: { tabId: tab.id },
        files: ["startWork.js"],
    });
});

startWork.js

let oldXHROpen = window.XMLHttpRequest.prototype.open;
console.log('script injected');
window.XMLHttpRequest.prototype.open = function() {
  this.addEventListener("load", function() {
    const responseBody = this.responseText;
    sessionStorage.setItem('payload', responseBody );
  });
  return oldXHROpen.apply(this, arguments);
};

manifest.json

{
    "name": "extTracker",
    "description": "want to save post request details",
    "version": "1.0",
    "manifest_version": 3,
    "background": {
        "service_worker": "background.js"
    },
    "host_permissions": ["<all_urls>"],
    "permissions": ["storage", "activeTab", "scripting"],
    "web_accessible_resources": [{
        "resources": ["/assets/media/blinkSd.mp3"],
        "matches": ["<all_urls>"],
        "use_dynamic_url": true
    }],
    "action": {
        "default_popup": "popup.html"
    }
}
失败原因分析
  1. 脚本注入时机滞后:点击按钮注入脚本时,页面中可能已经初始化了XMLHttpRequest实例,覆写只会影响之后新建的实例,无法拦截早于脚本注入的请求对象。
  2. 未捕获请求体且逻辑错位:当前代码监听load事件只能获取响应内容,完全没处理请求体(payload),也没判断请求方法是否为POST,根本没命中需求核心。
  3. 遗漏fetch API拦截:现代网页大量使用fetch而非XMLHttpRequest,你的代码只覆盖了XHR,漏掉了大部分可能的请求。
  4. 会话存储作用域限制:页面脚本和内容脚本虽共享sessionStorage,但如果页面有iframe或跨源子页面,可能出现存储/读取失败的情况。
更优实现方案

方案一:改进内容脚本,同时拦截XHR和fetch

直接在页面主上下文拦截所有请求,确保捕获下一次POST请求体:

修改后的startWork.js

// 标记是否需要拦截下一次POST请求
let shouldInterceptNextPost = true;

// 拦截XMLHttpRequest
const oldXHROpen = window.XMLHttpRequest.prototype.open;
const oldXHRSend = window.XMLHttpRequest.prototype.send;

window.XMLHttpRequest.prototype.open = function(method, url) {
  this._method = method;
  this._url = url;
  // 记录请求头(需覆写setRequestHeader)
  this._requestHeaders = {};
  const originalSetHeader = this.setRequestHeader;
  this.setRequestHeader = function(key, value) {
    this._requestHeaders[key] = value;
    originalSetHeader.apply(this, arguments);
  };
  return oldXHROpen.apply(this, arguments);
};

window.XMLHttpRequest.prototype.send = function(body) {
  if (shouldInterceptNextPost && this._method.toUpperCase() === 'POST') {
    const requestDetails = {
      method: this._method,
      url: this._url,
      headers: this._requestHeaders,
      requestBody: body
    };
    sessionStorage.setItem('nextPostRequest', JSON.stringify(requestDetails));
    shouldInterceptNextPost = false;
  }
  return oldXHRSend.apply(this, arguments);
};

// 拦截fetch API
const oldFetch = window.fetch;
window.fetch = async function(resource, options = {}) {
  const method = (options.method || 'GET').toUpperCase();
  if (shouldInterceptNextPost && method === 'POST') {
    const url = typeof resource === 'string' ? resource : resource.url;
    const headers = options.headers ? Object.fromEntries(options.headers.entries()) : {};
    let requestBody = null;
    if (options.body) {
      requestBody = await (new Response(options.body)).text();
    }
    const requestDetails = { method, url, headers, requestBody };
    sessionStorage.setItem('nextPostRequest', JSON.stringify(requestDetails));
    shouldInterceptNextPost = false;
  }
  return oldFetch.apply(this, arguments);
};

console.log('拦截脚本已注入,等待下一次POST请求');

调整popup.js注入时机

确保脚本在页面初始化前注入,避免遗漏请求:

chrome.scripting.executeScript({
    target: { tabId: tab.id },
    files: ["startWork.js"],
    world: 'MAIN', // 注入到页面主上下文,确保覆盖原生API
    runAt: 'document_start' // 在页面开始加载时注入
});

方案二:使用Chrome webRequest API(Manifest V3推荐)

无需注入内容脚本,通过后台服务工作者拦截请求,稳定性更强:

步骤1:更新manifest.json

添加必要权限:

{
    "name": "extTracker",
    "description": "保存POST请求详情",
    "version": "1.0",
    "manifest_version": 3,
    "background": {
        "service_worker": "background.js"
    },
    "host_permissions": ["<all_urls>"],
    "permissions": ["storage", "activeTab", "scripting", "webRequest", "webRequestBlocking"],
    "action": {
        "default_popup": "popup.html"
    }
}

步骤2:编写background.js

let interceptActive = false;
let targetTabId = null;

// 拦截请求发送事件
chrome.webRequest.onBeforeRequest.addListener(
  (details) => {
    if (interceptActive && details.tabId === targetTabId && details.method === 'POST') {
      let requestBody = '';
      // 解析请求体
      if (details.requestBody?.raw) {
        requestBody = new TextDecoder().decode(details.requestBody.raw[0].bytes);
      } else if (details.requestBody?.formData) {
        requestBody = JSON.stringify(details.requestBody.formData);
      }
      // 保存到扩展会话存储
      chrome.storage.session.set({
        nextPostRequest: {
          method: details.method,
          url: details.url,
          headers: details.requestHeaders,
          requestBody: requestBody
        }
      }).then(() => {
        interceptActive = false;
        console.log('已捕获并保存POST请求详情');
      });
    }
  },
  { urls: ["<all_urls>"], types: ["xmlhttprequest", "fetch"] },
  ["requestBody", "blocking"]
);

// 接收popup的启动指令
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === 'startIntercept') {
    interceptActive = true;
    targetTabId = message.tabId;
    sendResponse({ status: 'intercept started' });
  }
});

步骤3:修改popup.js

let startIntercept = document.getElementById("startIntercept");

startIntercept.addEventListener("click", async () => {
    let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    chrome.runtime.sendMessage({ action: 'startIntercept', tabId: tab.id }, (response) => {
      if (response.status === 'intercept started') {
        alert('已启动下一次POST请求拦截');
      }
    });
});

方案优势

  • 无需注入内容脚本,避开页面上下文隔离问题。
  • 同时支持XHR和fetch请求,覆盖所有常见请求方式。
  • 使用chrome.storage.session存储数据,扩展内各组件可统一读取。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 20:55:21