Chrome扩展:background.js发送XHR至Node.js服务器的问题咨询
解决Chrome扩展跨域请求与后台脚本API问题
核心误解澄清
Chrome扩展的后台脚本(Manifest V2的背景页/Manifest V3的Service Worker)并非不支持网络请求,只是API使用有版本差异:
- Manifest V2的背景页是普通浏览器环境,完全支持
XMLHttpRequest; - Manifest V3的Service Worker属于无DOM的隔离环境,不支持
XMLHttpRequest,需使用标准fetch API。
最优解决方案:后台脚本代理请求
由于内容脚本(content.js)运行在网页上下文,受CORS限制,而后台脚本属于扩展上下文,不受网页CORS约束,因此最佳方案是让content.js通过消息通信,委托后台脚本发送请求。
1. 适配Manifest V3(推荐版本)
后台脚本(background.js,Service Worker环境)
使用fetch实现请求逻辑,并监听来自content.js的消息:
// 向服务器发送请求的函数 async function sendToServer(message) { try { const res = await fetch('http://localhost:3000/data', { method: 'POST', headers: { 'Content-Type': 'application/json;charset=UTF-8' }, body: JSON.stringify(message) }); if (!res.ok) throw new Error(`请求失败:${res.status}`); return await res.json(); } catch (err) { console.error('服务器请求出错:', err); return null; } } // 监听来自content.js的消息 chrome.runtime.onMessage.addListener((req, sender, sendResponse) => { if (req.action === 'callServer') { sendToServer(req.msg).then(sendResponse); return true; // 告知Chrome需异步返回响应 } });
内容脚本(content.js)
通过消息API触发后台请求:
chrome.runtime.sendMessage( { action: 'callServer', msg: { message: 'Hello' } }, (response) => { if (response) console.log('服务器响应:', response.response); } );
2. 适配Manifest V2(旧版本)
后台配置(manifest.json)
确保背景页为普通HTML页面:
"background": { "page": "background.html" }
后台脚本(background.js)
可直接使用XMLHttpRequest或fetch:
// 使用XHR的示例 function sendToServer(message) { const xhr = new XMLHttpRequest(); xhr.open('POST', 'http://localhost:3000/data', true); xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { const res = JSON.parse(xhr.responseText); console.log(res.response); // 可通过chrome.runtime.sendMessage回传给content.js } }; xhr.send(JSON.stringify(message)); } // 监听content.js消息逻辑同V3版本
3. 服务器端代码修复
你的服务器代码存在两个关键问题:
- 模板字符串使用单引号,导致变量无法解析;
- 未处理浏览器预检OPTIONS请求,会导致content.js直接请求时CORS报错。
修复后的服务器代码:
const http = require('http'); const server = http.createServer((req, res) => { // 处理OPTIONS预检请求 if (req.method === 'OPTIONS') { res.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type' }); res.end(); return; } if (req.method === 'POST' && req.url === '/data') { let body = ''; req.on('data', chunk => body += chunk.toString()); req.on('end', () => { const receivedMsg = JSON.parse(body).message; console.log(`Received message: ${receivedMsg}`); // 改用反引号解析变量 res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ response: `Server received: ${receivedMsg}` // 修复模板字符串 })); }); } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found'); } }); server.listen(3000, () => console.log('Server running on port 3000'));
内容的提问来源于stack exchange,提问作者user25899957
相关产品推荐
相关产品推荐

