Google Cloud Functions配置CORS响应头解决跨域请求问题
问题场景
前端通过按钮触发fetch请求调用部署在Google Cloud Functions(GCF)的后端接口时,浏览器控制台出现CORS相关错误:
从源'前端URL'访问'GCF后端URL'的fetch请求被CORS策略阻止:预检请求的响应未通过访问控制检查:请求的资源上不存在'Access-Control-Allow-Origin'标头。如果不需要透明响应,可将请求模式设置为'no-cors'以禁用CORS获取资源。
POST /后端URL/ net::ERR_FAILED
Error sending message: TypeError: Failed to fetch
请求情况:
- 第一个是带预检的POST请求,返回CORS错误
- 第二个是OPTIONS请求,返回400状态码
已尝试在GCF后端设置Access-Control-Allow-Origin: *,但问题依旧。
后端代码(Node.js)
const functions = require('@google-cloud/functions-framework'); functions.http('sendEmail', async (req, res) => { //req body validation and desctructuring try { //logic handling data from request // Set CORS headers res.set('Access-Control-Allow-Origin', '*'); if (req.method === 'OPTIONS') { res.set('Access-Control-Allow-Methods', 'GET, POST'); res.set('Access-Control-Allow-Headers', 'Content-Type'); res.set('Access-Control-Max-Age', '3600'); res.status(204).send(''); } else { res.status(200).json({ message: 'Message sent successfully' }); } } catch (error) { console.error('Error sending email:', error); res.status(500).json({ error: 'Failed to send email' }); } });
前端fetch代码
try { const response = await fetch( "backend URL", { method: "POST", headers: { "Content-Type": "application/json", "Accept":"*/*" }, body: JSON.stringify({ /*data to send*/ }), } ); const data = await response.json(); if (response.ok) { alert(data.message); } else { alert("Failed to send message. Please try again later."); } } catch (error) { console.error("Error sending message:", error); alert("Failed to send message. Please try again later."); }
已尝试的操作
- Postman发送POST/OPTIONS请求均正常,响应头包含设置的CORS字段
- 前端添加
credentials:"include"无效果 - 重新部署GCF后端无效
- 确认GCF允许未认证调用
- 查看GCF请求日志仅能区分Postman与浏览器请求,无有效错误信息
问题原因与解决方法
1. CORS头设置时机错误
当前代码中CORS头是在try块内设置的,如果请求在进入try块前就抛出错误(比如请求体解析失败),就不会返回CORS头,导致预检失败。
修复方案:将CORS头设置逻辑移到try块外,确保所有请求(包括OPTIONS和出错的请求)都能拿到CORS头:
const functions = require('@google-cloud/functions-framework'); functions.http('sendEmail', async (req, res) => { // 先设置基础CORS头,确保所有请求都能获取 res.set('Access-Control-Allow-Origin', '*'); if (req.method === 'OPTIONS') { res.set('Access-Control-Allow-Methods', 'GET, POST'); res.set('Access-Control-Allow-Headers', 'Content-Type'); res.set('Access-Control-Max-Age', '3600'); return res.status(204).send(''); } try { //req body validation and desctructuring //logic handling data from request res.status(200).json({ message: 'Message sent successfully' }); } catch (error) { console.error('Error sending email:', error); res.status(500).json({ error: 'Failed to send email' }); } });
2. OPTIONS请求返回400的可能原因
GCF的HTTP函数默认会解析请求体,若OPTIONS请求带有Content-Type: application/json头,函数可能尝试解析空的请求体导致报错,返回400。上面的修复中,先处理OPTIONS请求并直接返回,跳过后续的请求体解析逻辑,能解决这个问题。
3. 前端请求优化
移除Accept: */*头(浏览器默认会发送合适的Accept头,无需手动设置),避免额外的预检触发:
try { const response = await fetch( "backend URL", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ /*data to send*/ }), } ); const data = await response.json(); if (response.ok) { alert(data.message); } else { alert("Failed to send message. Please try again later."); } } catch (error) { console.error("Error sending message:", error); alert("Failed to send message. Please try again later."); }
4. 验证部署
修改代码后,重新部署GCF函数:
gcloud functions deploy sendEmail --runtime nodejs20 --trigger-http
部署完成后,用浏览器再次测试请求,确认CORS错误消失。
内容的提问来源于stack exchange,提问作者Alchemist4444

