Chrome扩展GET请求拦截功能失效,请求排查问题
排查Chrome扩展GET请求拦截失效问题
当前开发的Chrome扩展(Manifest V3)本应拦截GET请求,但存在以下问题:
- 页面控制台和服务工作者控制台均看不到content.js开头的
console.log("Content script running")输出,尽管资源列表显示content.js已加载 - 服务工作者未收到content.js发送的任何消息,拦截代码完全未被触发
- 已确认
valid-domains.txt和intercept-file.txt文件存在且内容有效
相关代码
manifest.json
{ "manifest_version": 3, "name": "GET Request Interceptor", "version": "1.0", "permissions": [ "webRequest", "activeTab" ], "background": { "service_worker": "background.js" }, "host_permissions": [ "<all_urls>" ], "icons": { "16": "icons/icon.svg", "48": "icons/icon.svg", "128": "icons/icon.svg" }, "action": { "default_icon": "icons/icon.svg" }, "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content.js"], "run_at": "document_start" } ] }
background.js
let validDomains = []; let interceptRegex = null; // Load valid domains and intercept regex at startup fetch(chrome.runtime.getURL('valid-domains.txt')) .then((response) => response.text()) .then((text) => { validDomains = text.split('\n').map(domain => domain.trim()).filter(domain => domain); console.log('Valid domains loaded:', validDomains); }) .catch((error) => console.error('Failed to load valid domains:', error)); // Load the regex for path matching from intercept-file.txt fetch(chrome.runtime.getURL('intercept-file.txt')) .then((response) => response.text()) .then((regexText) => { try { // Create a regular expression object from the file content interceptRegex = new RegExp(regexText.trim()); console.log('Intercept regex loaded:', interceptRegex); } catch (error) { console.error('Invalid regex in intercept-file.txt:', error); } }) .catch((error) => console.error('Failed to load intercept regex:', error)); // Helper function to extract the root domain from a hostname function getRootDomain(hostname) { const parts = hostname.split('.').reverse(); if (parts.length >= 2) { return `${parts[1]}.${parts[0]}`; } return hostname; // Fallback for localhost or unusual domains } // Transform function to modify the original content function transformContent(originalContent) { return originalContent; // Just return original content for now } // Initialize message listeners function setupMessageListeners() { const messageListener = (message, sender, sendResponse) => { if (message.type === 'intercept-fetch' || message.type === 'intercept-xmlhttp') { const { url, method } = message; // Fetch the original content fetch(url, { method: method || 'GET' }) .then((response) => response.text().then((body) => ({ response, body }))) .then(({ response, body }) => { const parsedUrl = new URL(url); const domain = getRootDomain(parsedUrl.hostname); const path = parsedUrl.pathname; console.log("Intercepted GET request: ", domain, path); // Check if the domain is valid if (validDomains.includes(domain) && interceptRegex.test(path)) { // Transform content if regex matches const transformedContent = transformContent(body); sendResponse({ status: 200, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), body: transformedContent }); } else { // Return original content if no match sendResponse({ status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), body }); } }) .catch((error) => { console.error('Error fetching original content:', error); sendResponse({ status: 404, statusText: 'Cannot find content', body: '' }); }); return true; // Keep the message channel open for async response } }; // Register the message listener chrome.runtime.onMessage.addListener(messageListener); } // Initialize listeners when the worker starts setupMessageListeners();
content.js
console.log("Content script running"); // Intercept fetch API const originalFetch = window.fetch; window.fetch = async (...args) => { const [resource, config] = args; // Send the request details to the service worker const message = { type: 'intercept-fetch', url: resource, method: config?.method || 'GET', headers: config?.headers, body: config?.body }; // Send message to service worker and wait for response const response = await new Promise((resolve) => { chrome.runtime.sendMessage(message, resolve); }); if (response && response.status) { // Construct a Response object with the data from the service worker return new Response(response.body, { status: response.status, statusText: response.statusText, headers: new Headers(response.headers) }); } // Fallback to original fetch if no valid response received return originalFetch(...args); }; // Intercept XMLHttpRequest const originalXHROpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (method, url, async = true, user, password) { this._intercepted = { method, url }; return originalXHROpen.apply(this, arguments); }; const originalXHRSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function (body) { if (this._intercepted) { const { method, url } = this._intercepted; // Send the request details to the service worker chrome.runtime.sendMessage( { type: 'intercept-xmlhttp', url, method, body }, (response) => { if (response && response.status) { // Replace the XHR response Object.defineProperty(this, 'responseText', { get: () => response.body, }); this.status = response.status; this.statusText = response.statusText; // Trigger readystatechange event this.readyState = 4; this.onreadystatechange && this.onreadystatechange(); this.onload && this.onload(); } else { // Fallback to original XHR send if no valid response received originalXHRSend.apply(this, arguments); } } ); } else { // No interception, proceed with original send originalXHRSend.apply(this, arguments); } };
排查步骤
验证content脚本注入状态
- 确认扩展已获得当前测试站点的权限:进入扩展管理页面(chrome://extensions/),找到该扩展,点击"详细信息",查看"网站权限"是否包含目标站点,特殊站点如
chrome://、chrome-extensions://默认不允许content脚本注入 - 临时修改
run_at为document_idle,避免document_start时机过早导致脚本注入失败,测试后再改回原设置
- 确认扩展已获得当前测试站点的权限:进入扩展管理页面(chrome://extensions/),找到该扩展,点击"详细信息",查看"网站权限"是否包含目标站点,特殊站点如
添加消息发送错误捕获
content脚本中调用chrome.runtime.sendMessage时未处理错误,需添加捕获逻辑定位问题:
修改fetch拦截部分的消息发送代码:const response = await new Promise((resolve, reject) => { chrome.runtime.sendMessage(message, (response) => { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); console.error("Fetch消息发送失败:", chrome.runtime.lastError); } else { resolve(response); } }); }).catch(err => { console.error("Fetch拦截失败:", err); return null; });修改XHR拦截部分的消息发送代码:
chrome.runtime.sendMessage( { type: 'intercept-xmlhttp', url, method, body }, (response) => { if (chrome.runtime.lastError) { console.error("XHR消息发送失败:", chrome.runtime.lastError); originalXHRSend.apply(this, arguments); return; } // 原response处理逻辑 } );修复资源加载时机问题
当前validDomains和interceptRegex是异步加载,若请求在资源加载完成前触发,会导致拦截逻辑失效,需等待资源加载完成后再处理请求:let validDomains = []; let interceptRegex = null; let isResourcesLoaded = false; // 使用Promise.all等待所有资源加载完成 Promise.all([ fetch(chrome.runtime.getURL('valid-domains.txt')) .then(res => res.text()) .then(text => { validDomains = text.split('\n').map(d => d.trim()).filter(Boolean); console.log('Valid domains loaded:', validDomains); }), fetch(chrome.runtime.getURL('intercept-file.txt')) .then(res => res.text()) .then(regexText => { interceptRegex = new RegExp(regexText.trim()); console.log('Intercept regex loaded:', interceptRegex); }) ]).then(() => { isResourcesLoaded = true; console.log("所有资源加载完成,开始处理请求"); }).catch(err => { console.error("资源加载失败:", err); }); // 修改消息监听逻辑 const messageListener = (message, sender, sendResponse) => { if (!isResourcesLoaded) { console.log("资源未加载完成,跳过当前请求"); sendResponse(null); return true; } // 原请求处理逻辑 };确认控制台查看位置
content脚本的console.log输出会出现在当前页面的开发者工具控制台(按F12打开,切换到"Console"标签),而非扩展的服务工作者控制台,需确认查看的是正确的控制台。
内容的提问来源于stack exchange,提问作者markt1964
相关产品推荐
相关产品推荐

