启用CORS仍遇Access-Control-Allow-Origin错误,树莓派植物控水网站问题
问题背景
在树莓派上搭建了基于Node.js的植物控水网站,通过数据库实现数据交互:Node.js连接数据库展示表格,用户修改表格内容后同步到数据库。网站在树莓派本地localhost:3000可正常运行,但用同一局域网的电脑访问(通过树莓派IP192.168.0.101:3000)时,出现Socket.io相关的CORS跨域错误。
错误信息
Access to XMLHttpRequest at 'http://localhost:3000/socket.io/?EIO=4&transport=polling&t=OvqJ4FV' from origin 'http://192.168.0.101:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
manager.js:108GET http://localhost:3000/socket.io/?EIO=4&transport=polling&t=OvqJ4FV net::ERR_FAILEDvalue @ polling.js:298
i @ polling.js:237
value @ polling.js:190
value @ polling.js:215
value @ polling.js:96
value @ polling.js:56
value @ transport.js:46
value @ socket.js:170
a @ socket.js:111
value @ manager.js:108
(anonymous) @ manager.js:328
Show 9 more frames
Show less
当前代码(main.cjs)
const express = require('express'); const axios = require('axios'); const http = require('http'); const socketIo = require('socket.io'); var mysql = require('mysql2'); var plantData = mysql.createPool({ host: 'localhost', user: 'root', password: '', database: "plants", waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); const app = express(); const cors = require("cors"); const port2 = 5110; app.listen(port2); console.log("" + 3000); app.use("*", cors()); const server = http.createServer(app); server.listen(3000); var io = socketIo(server); app.set("view engine", "ejs"); app.set("views", "src2"); app.use(express.static("src2")); app.get("/", (req, res) => { res.render("ejs/main.ejs"); res.setHeader("Allow-Access-Control-Header", "*"); }); var howManyPlants = 0; app.get("/plants", (req, res) => { return new Promise((resolve, reject) => { plantData.query("SELECT * FROM `plants` order by id asc" , (err, results) => { if (err) { console.log(err); } else { resolve(results); } var plants = results; //res.setHeader("Allow-Access-Control-Header", "*"); res.json(plants); howManyPlants = plants.length; }); }); }); app.get("/maxPlantId", (req, res) => { new Promise((resolve, reject) => { plantData.query(`Select max(id) from plants`, (err, results) => { if (err) { console.log(err); } else { resolve(results); plantIdNow = res.json(results); } }); }); }); io.on("connection", (socket) => { socket.on("setAllPlantsTime", (time) => { return new Promise((resolve, reject) => { console.log("1"); plantData.query(`UPDATE plants set plantTime = "${time}"`, (err, results) => { if (err) { console.log(err); } else { resolve(results); } }); }); }); socket.on("changePlantProperty", (property) => { var plantId = property.id; var plantProperty = property.property; var plantPropertyValue = property.value; console.log(plantId + " " + plantProperty + " " + plantPropertyValue); return new Promise((resolve, reject) => { plantData.query(`UPDATE plants set ${plantProperty} = "${plantPropertyValue}" where id = ${plantId}`, (err, results) => { console.log(`UPDATE plants set ${plantProperty} = "${plantPropertyValue}" where id = ${plantId}`); if (err) { console.log(err); } else { resolve(results); } }); }); }); socket.on("deletePlant", (plantId) => { return new Promise((resolve, reject) => { plantData.query(`Delete from plants where id = ${plantId}`, (err, results) => { if (err) { console.log(err); } else { resolve(results); } }); }); }); socket.on("createPlant", (plantInput) => { var plantId = plantInput.id; var plantTime = plantInput.plantTime; var plantWeekDays = plantInput.plantWeekDays; var plantWater = plantInput.plantWater; console.log(plantId + " " + plantTime + " " + plantWeekDays + " " + plantWater); return new Promise((resolve, reject) => { plantData.query(`INSERT INTO plants VALUES ('` + plantId +`','` + plantTime + `','` + plantWeekDays + `','` + plantWater + `')`, (err, results) => { if (err) { console.log(err); } else { resolve(results); } }); }); }); socket.on('disconnect', () => { console.log('disconnected'); }); }); module.exports = { io }
已尝试操作
- 参考多种CORS相关教程配置,问题未解决
- 当前使用XAMPP环境
解决方法
问题核心是Socket.io未配置CORS规则——你仅给Express应用配置了CORS,但Socket.io有独立的CORS配置项,需单独设置:
- 修改Socket.io初始化代码,添加CORS配置:
// 替换原有的var io = socketIo(server); var io = socketIo(server, { cors: { origin: "*", // 开发环境可临时用*,生产环境建议指定具体IP/域名,比如"http://192.168.0.101:3000" methods: ["GET", "POST"] } });
- 额外优化点:
- 代码中同时启动了
port2=5110和3000两个端口,若不需要可删除app.listen(port2);,避免资源浪费 res.setHeader("Allow-Access-Control-Header", "*");是错误的头名称,正确应为Access-Control-Allow-Origin,且已通过cors中间件配置,可直接删除该行- 数据库操作的Promise写法可优化,比如用
util.promisify转换mysql的query方法,简化嵌套逻辑
- 代码中同时启动了
验证修改
重启Node.js服务后,用电脑访问树莓派IP的3000端口,Socket.io跨域请求即可正常执行,不会再出现CORS错误。
内容的提问来源于stack exchange,提问作者Antoni Nabzdyk

