部署至Google App Engine后调用child_process触发CORS错误排查
问题描述
我搭了个Express服务器,本地运行完全正常,但部署到Google App Engine后发送请求就被CORS策略拦截。只要把exec那段代码注释掉,直接返回响应就没这个问题,这是为啥?
我的代码
const express = require('express'); const cors = require('cors'); const bodyParser = require('body-parser'); const { exec } = require('child_process'); const app = express(); app.use(bodyParser.json()); app.use(cors()); module.exports = app app.post("/", (req,res) =>{ console.log("Get from /"); console.log(req.body.data) // 注释掉这段exec就正常 exec('npx hardhat run scripts/deploy.js --network goerli', (error, stdout, stderr) => { if (error !== null) { console.log(`exec error: ${error}`); } else{ res.send("response"); } }) }); app.listen(8080, () => { console.log('listening on port 8080'); });
package.json
{ "name": "hardhat-project", "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.6", "ethers": "^5.6.9", "hardhat": "^2.9.9" }, "version": "1.0.0", "description": "smart contract", "main": "hardhat.config.js", "dependencies": { "cors": "^2.8.5", "express": "^4.18.1", "firebase-admin": "^11.0.0" }, "scripts": { "start": "node src/index.js", "test": "mocha" }, "author": "", "license": "ISC" }
错误截图

原因分析
请求超时未响应,浏览器误判为CORS错误
Google App Engine对请求有默认超时限制(一般60秒),而用npx hardhat run部署合约到Goerli测试网大概率会超时。exec是异步操作,还没等到回调触发返回响应,GAE就把连接掐了,浏览器收不到任何响应,就会以CORS拦截的形式报错。去掉exec直接返回,请求秒完成,自然没这个问题。依赖未安装导致exec执行失败,无响应返回
你的hardhat放在devDependencies里,GAE部署时默认不会安装dev依赖,导致找不到hardhat命令,exec直接报错。但你的代码只在控制台打印错误日志,没给客户端返回任何响应,请求一直挂着,浏览器就触发CORS拦截。GAE环境限制无法执行外部命令
GAE标准环境对进程执行管得很严,可能不允许通过exec调用npx、hardhat这类外部命令。这种情况下exec直接失败,同样因为没返回响应,导致浏览器报CORS错。
解决办法
1. 把hardhat移到dependencies
修改package.json,把hardhat及其相关依赖从devDependencies挪到dependencies,确保部署时能装上:
{ "dependencies": { "@nomiclabs/hardhat-ethers": "^2.0.6", "ethers": "^5.6.9", "hardhat": "^2.9.9", "cors": "^2.8.5", "express": "^4.18.1", "firebase-admin": "^11.0.0" } }
2. 处理exec错误,确保返回响应
不管exec成功还是失败,都要给客户端返回响应,别让请求挂着:
exec('npx hardhat run scripts/deploy.js --network goerli', (error, stdout, stderr) => { if (error !== null) { console.log(`exec error: ${error}`); res.status(500).send(`部署失败: ${error.message}`); } else { res.send("部署成功"); } })
3. 改用hardhat API直接部署,避免exec
别在GAE里调用外部命令,直接用Node.js代码引入hardhat API执行部署:
const hre = require("hardhat"); app.post("/", async (req,res) =>{ try { const Contract = await hre.ethers.getContractFactory("YourContract"); const contract = await Contract.deploy(); await contract.deployed(); res.send(`合约部署成功,地址: ${contract.address}`); } catch (error) { console.error(error); res.status(500).send(`部署失败: ${error.message}`); } });
4. 调整GAE请求超时(不推荐)
如果非要保留exec,可以在app.yaml里延长超时时间(最大24小时,但合约部署不该是同步长请求):
runtime: nodejs16 handlers: - url: /.* script: auto timeout: 300s # 设置为5分钟
内容的提问来源于stack exchange,提问作者Nina

