React集成Firebase Cloud Messaging推送通知问题求助
问题排查与修复方案
一、推送通知失效问题(本地偶现、Netlify完全失效)
1. 前端直接调用FCM API的跨域与密钥泄露问题
FCM的fcm/send接口禁止前端直接调用,浏览器跨域策略会拦截请求:本地开发环境因浏览器宽松策略偶尔绕过,但部署到Netlify后会被严格拦截。同时,前端直接暴露FCM服务器密钥会引发安全风险。
修复方案:
- 将
sendNotification逻辑迁移到后端(如Node.js、Firebase Functions),前端调用自有后端接口,由后端转发请求到FCM API。 - 后端示例(Node.js + Express):
const axios = require('axios'); require('dotenv').config(); app.post('/send-notification', async (req, res) => { const { token, message } = req.body; const headers = { 'Authorization': `key=${process.env.FCM_SERVER_KEY}`, 'Content-Type': 'application/json' }; const fcmMessage = { to: token, content_available: true, apns_priority: 5, notification: { body: message } }; try { await axios.post('https://fcm.googleapis.com/fcm/send', fcmMessage, { headers }); res.status(200).send('Notification sent'); } catch (err) { res.status(500).send(err.message); } });
- 前端修改
sendNotification为调用后端接口:
async function sendNotification(id, userMessage) { await axios.post('/send-notification', { token: id, message: `Admin : ${userMessage}` }); }
2. 代码语法错误导致请求失败
你的sendNotification函数中,message对象存在语法缺失:notification节点未闭合大括号,整个message对象也未闭合,会导致JSON格式错误,请求被FCM直接拒绝。
修复后的message对象:
const message = { 'to': `${id}`, 'content_available': true, 'apns_priority': 5, 'notification': { body: `${userMessage}` } // 补上缺失的闭合括号 }; // 补上整个message对象的闭合括号
3. 未捕获请求异常
当前代码未处理axios.post的错误,无法定位失败原因,添加错误捕获便于调试:
async function sendNotification(id, userMessage) { // ... 其他代码 try { await axios.post(url, message, { headers: headers }); console.log('Notification sent successfully'); } catch (err) { console.error('Notification failed:', err.response?.data || err.message); } }
二、重复生成/写入相同Token问题
1. useEffect无依赖数组导致重复执行
你的useEffect未设置依赖数组,组件每次渲染都会触发onAuthStateChanged回调,进而重复调用requestPermission和callToken,导致重复获取Token并写入数据库。
修复方案:给useEffect添加依赖数组,仅在必要时执行,同时清理订阅避免内存泄漏:
useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (data) => { if(!data) { goTo('/login') } else { currentBalance(); requestPermission(); } }); return () => unsubscribe(); }, [auth, goTo, currentBalance]); // 添加依赖数组
2. 重复写入相同Token到数据库
getToken在同一设备、同一用户下会返回相同Token,无需每次更新数据库。先读取数据库中的Token对比,仅在不同时更新:
async function callToken() { try { const code = await getToken(messaging, { vapidKey: process.env.NOTIFICATION }); if (!code) return; const userDoc = doc(database, "users", auth.currentUser.email); const docSnapshot = await getDoc(userDoc); if (docSnapshot.exists() && docSnapshot.data().notice === code) { // Token未变化,无需更新 return; } await updateDoc(userDoc, { notice: code }); } catch (err) { console.error('Failed to get/update token:', err); } }
3. 混合使用async/await与then导致逻辑混乱
统一使用async/await提升代码可读性与执行顺序稳定性:
// 修复后的callToken async function callToken() { try { const code = await getToken(messaging, { vapidKey: process.env.NOTIFICATION }); if (!code) { console.log('Failed to get FCM token'); return; } const dc = doc(database, "users", auth.currentUser.email); await updateDoc(dc, { notice: code }); } catch (err) { console.error('Error handling token:', err); } } // 修复后的requestPermission async function requestPermission() { const permission = await Notification.requestPermission(); if (permission === 'granted') { console.log('Notification permission granted.'); await callToken(); } }
额外检查项
- Netlify环境变量:确认
NOTIFICATION(VAPID密钥)在Netlify环境变量中正确配置,名称与代码一致。 - FCM配置:检查Firebase控制台中Web应用的VAPID密钥是否正确配置,服务器密钥未泄露。
- HTTPS要求:FCM通知要求站点使用HTTPS,Netlify默认提供HTTPS,本地开发需使用
localhost或HTTPS代理。
内容的提问来源于stack exchange,提问作者user19394650
相关产品推荐
相关产品推荐

