Chrome扩展:拦截修改JSON payload并前置处理DOM渲染的技术问询
问题描述
我正在为自有网站开发一款Chrome扩展,目标是让用户在浏览器端补全数据(隐私友好,无需后端参与逻辑,也不访问用户数据),期望流程如下:
- 用户请求内容时,后端仅返回字符串"Animal",对应JSON为
{"userWants": "Animal"}(出于隐私考虑,我方不知道用户偏好猫咪); - 浏览器接收该JSON后,将
userWants字段值修改为"Cat"; - 最终页面显示"Cat"而非默认的"Animal"。
我尝试过以下JS代码(可忽略):
console.log("Im here!"); function modifyJSON(json) { // Modify the JSON object here as needed // Example: json.title = "Modified Title"; json.userWants = "Cat"; return json; } const originalFetch = window.fetch; window.fetch = async function (...args) { const response = await originalFetch.apply(this, args); if (response.url.endsWith("/fetch-json")) { const clonedResponse = response.clone(); const json = await response.json(); const modifiedJSON = modifyJSON(json); return new Response(JSON.stringify(modifiedJSON), { status: clonedResponse.status, statusText: clonedResponse.statusText, headers: clonedResponse.headers, }); } else { return response; } };
现在想了解:能在JSON payload到达DOM元素前拦截并修改内容,最终显示"Cat"的相关技术、API或实现方法。
可行实现方法
1. 优化Fetch API拦截(你当前的方案)
你的现有思路是对的,但要确保拦截逻辑在页面脚本之前生效,同时完善异常处理:
- 在扩展
manifest.json里设置"run_at": "document_start",让内容脚本优先于页面脚本加载,避免页面fetch先被初始化; - 增加错误捕获,防止解析JSON失败导致请求异常;
- 只针对目标接口做修改,不影响其他请求。
优化后的代码示例:
(function() { const originalFetch = window.fetch; window.fetch = async function(...args) { try { const response = await originalFetch.apply(this, args); // 仅拦截目标接口 if (response.url.endsWith("/fetch-json")) { const clonedResp = response.clone(); let jsonData; try { jsonData = await clonedResp.json(); // 修改指定字段 if (jsonData.userWants) { jsonData.userWants = "Cat"; } // 返回修改后的响应 return new Response(JSON.stringify(jsonData), { status: response.status, statusText: response.statusText, headers: response.headers }); } catch (e) { // 解析JSON失败,直接返回原响应 return response; } } return response; } catch (e) { // 请求失败,抛出原错误 throw e; } }; })();
2. 拦截XMLHttpRequest(兼容旧页面)
如果你的网站仍在使用XMLHttpRequest而非fetch,需要单独做拦截:
(function() { const originalXHR = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { this.addEventListener('load', function() { if (url.endsWith("/fetch-json") && (this.responseType === '' || this.responseType === 'json')) { try { const jsonData = JSON.parse(this.responseText); if (jsonData.userWants) { jsonData.userWants = "Cat"; // 重写响应文本和response对象 Object.defineProperty(this, 'responseText', { value: JSON.stringify(jsonData) }); if (this.response) { Object.defineProperty(this, 'response', { value: jsonData }); } } } catch (e) {} } }); originalXHR.apply(this, arguments); }; })();
3. Chrome扩展webRequest API(底层拦截)
用Chrome扩展的webRequest API可以在请求到达页面之前就修改响应,不需要注入内容脚本到页面:
- 在
manifest.json中添加权限:
{ "permissions": ["webRequest", "webRequestBlocking", "https://your-domain.com/*"], "background": { "service_worker": "background.js" } }
- 在
background.js中实现拦截:
// 移除Content-Length头,避免修改内容后长度不匹配 chrome.webRequest.onHeadersReceived.addListener( function(details) { const contentType = details.responseHeaders.find(h => h.name.toLowerCase() === 'content-type'); if (contentType && contentType.value.includes('application/json') && details.url.endsWith("/fetch-json")) { return { responseHeaders: details.responseHeaders.map(h => { if (h.name.toLowerCase() === 'content-length') return undefined; return h; }).filter(Boolean) }; } }, {urls: ["https://your-domain.com/*"]}, ["blocking", "responseHeaders"] ); // 拦截响应体并修改 chrome.webRequest.onResponseStarted.addListener( async function(details) { if (details.url.endsWith("/fetch-json")) { // 获取原响应内容 const response = await fetch(details.url, { headers: details.requestHeaders, method: details.method, credentials: 'include' }); const jsonData = await response.json(); jsonData.userWants = "Cat"; // 创建修改后的Blob并返回 const blob = new Blob([JSON.stringify(jsonData)], {type: 'application/json'}); const url = URL.createObjectURL(blob); return {redirectUrl: url}; } }, {urls: ["https://your-domain.com/*"]}, ["blocking"] );
注意:这种方法需要处理跨域和缓存问题,适合复杂场景。
4. DOM修改兜底方案(拦截请求失败时用)
如果以上方法都无法生效,可以在内容渲染到DOM后立即修改,虽然可能有短暂闪烁,但兼容性最好:
// 监听DOM变化 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { // 处理文本节点 if (node.nodeType === Node.TEXT_NODE && node.textContent.includes("Animal")) { node.textContent = node.textContent.replace("Animal", "Cat"); } // 处理元素节点的子内容 else if (node.nodeType === Node.ELEMENT_NODE) { node.querySelectorAll("*").forEach(el => { if (el.textContent.includes("Animal")) { el.textContent = el.textContent.replace("Animal", "Cat"); } }); } }); }); }); // 启动观察者 observer.observe(document.body, { childList: true, subtree: true, characterData: true });
内容的提问来源于stack exchange,提问作者NotYourClone
相关产品推荐
相关产品推荐

