Firebase云函数调用Google API失败,求GIS及邮件实现指南
问题解决:Firebase云函数调用Google API返回INTERNAL错误及邮件功能实现指引
一、云函数返回Google API响应失败的原因及修复
你的云函数代码存在两个核心问题,导致返回INTERNAL错误:
1. 授权头变量未正确解析
你在Authorization头中直接写了'Bearer data.accessToken',这是字符串字面量,没有实际注入传入的accessToken值,Google API会因无效令牌拒绝请求。需要用模板字符串拼接变量:
'Authorization': `Bearer ${data.accessToken}`
2. 直接返回HTTP响应流而非解析后的数据
https.get的response是可读流对象,不能直接作为云函数返回值(Firebase Callable函数要求返回可序列化的JSON数据)。需要读取响应体并解析为JSON后再返回。
修正后的云函数代码
exports.loadCalendar = functions.https.onCall(async (data, context) => { console.log('Manual Logging: loadCalendar started'); console.log('Data:', data); const https = require('https'); return new Promise((resolve, reject) => { const req = https.get({ hostname: 'googleapis.com', path: '/calendar/v3/calendars/primary/events', headers: { 'Authorization': `Bearer ${data.accessToken}`, }, }); req.on('response', (res) => { let body = ''; // 读取响应流 res.on('data', (chunk) => { body += chunk; }); // 响应结束后解析JSON res.on('end', () => { try { const result = JSON.parse(body); resolve(result); } catch (err) { reject(new Error(`Failed to parse response: ${err.message}`)); } }); }); req.on('error', (err) => { reject(new Error(`Request failed: ${err.message}`)); }); }); });
组件代码优化
httpsCallable返回的是Observable,无需同时使用await和subscribe,可以简化为:
loadCalendar() { this.fns.httpsCallable('loadCalendar')({ accessToken: this.access_token }) .subscribe({ next: (result) => console.log('result', result), error: (err) => console.error('error', err) }); }
二、邮件功能实现及新版GIS实践指引
1. 核心配置要点
要实现类似WP Mail SMTP的邮件发送,你需要:
- 确保用户授权的OAuth2 scope包含
https://www.googleapis.com/auth/gmail.send(仅发送权限,无需完全访问Gmail) - 在云函数中调用Gmail API的
users.messages.send接口发送邮件
2. 新版GIS实践建议
- 使用Google Identity Services客户端库获取令牌,遵循PKCE流程(适用于单页应用),确保令牌安全性和合规性
- 处理令牌过期:GIS会自动刷新令牌(请求
offline_accessscope后),无需手动管理刷新流程 - 权限最小化:仅请求邮件发送所需的
gmail.sendscope,避免过度授权
邮件发送云函数示例(基于Gmail API)
exports.sendEmail = functions.https.onCall(async (data, context) => { const { accessToken, to, subject, text } = data; const https = require('https'); // 构建Gmail API请求体(需base64编码) const emailContent = [ `To: ${to}`, `Subject: ${subject}`, '', text ].join('\n'); const encodedEmail = Buffer.from(emailContent).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); return new Promise((resolve, reject) => { const req = https.request({ hostname: 'googleapis.com', path: '/gmail/v1/users/me/messages/send', method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' } }, (res) => { let body = ''; res.on('data', (chunk) => body += chunk); res.on('end', () => { try { resolve(JSON.parse(body)); } catch (err) { reject(new Error(`Failed to parse response: ${err.message}`)); } }); }); req.on('error', (err) => reject(new Error(`Request failed: ${err.message}`))); req.write(JSON.stringify({ raw: encodedEmail })); req.end(); }); });
内容的提问来源于stack exchange,提问作者DE-AS360
相关产品推荐
相关产品推荐

