You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 13:15:24