Firebase Functions无法推送通知:token输出为undefined问题排查
解决Firebase Functions获取设备Token为undefined的问题
看起来你在这里踩了一个很常见的坑——你的函数监听的是/User这个根节点,但这个节点大概率是多个用户对象的集合,而不是单个用户的数据,所以直接访问notificationSnapshot.token自然拿不到值。
问题根源:数据结构不匹配
你可以仔细看一下console.info(notificationSnapshot)的输出内容,它应该是类似这样的层级结构(假设你的Realtime Database存储了多个用户):
{ "user_abc123": { "token": "fcm_token_xxx", "name": "张三", "surname": "张" }, "user_def456": { "token": "fcm_token_yyy", "name": "李四", "surname": "李" } }
这个顶层对象本身并没有token属性,token是每个子用户对象的专属属性,所以notificationSnapshot.token返回undefined完全合理。
两种解决方案,根据你的需求选择
方案1:给所有注册用户推送通知
如果你的目标是当/User节点有变化时,给所有用户批量发通知,那需要遍历这个用户集合里的每一个用户,逐个获取Token并发起推送:
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers. const functions = require('firebase-functions'); // The Firebase Admin SDK to access the Firebase Realtime Database. const admin = require('firebase-admin'); admin.initializeApp(); exports.sendSampleNotification = functions.database.ref('/User') .onWrite((change, context) => { const usersCollection = change.after.val(); // 获取所有用户的集合 if (!usersCollection) { console.log("No users found in /User node"); return null; } // 用Promise数组管理所有推送请求,确保函数等待所有操作完成 const pushPromises = []; // 遍历每个用户 for (const userId in usersCollection) { const userData = usersCollection[userId]; const userToken = userData.token; // 跳过没有Token的用户,避免无效请求 if (!userToken) { console.log(`User ${userId} has no token, skipping`); continue; } const message = { notification: { title: 'test title', body: 'test message' }, token: userToken }; // 将推送请求加入Promise数组 const pushPromise = admin.messaging().send(message) .then((response) => console.log(`Message sent to ${userId}:`, response)) .catch((error) => console.log(`Error sending to ${userId}:`, error)); pushPromises.push(pushPromise); } // 等待所有推送完成,返回Promise让函数正常结束 return Promise.all(pushPromises); });
方案2:单个用户更新时推送通知
如果你的需求是当某个用户的数据更新时,只给这个用户发通知,那应该把监听路径改成/User/{userId}(通过通配符匹配单个用户节点),这样change.after.val()就是单个用户的完整对象,直接取token就没问题了:
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers. const functions = require('firebase-functions'); // The Firebase Admin SDK to access the Firebase Realtime Database. const admin = require('firebase-admin'); admin.initializeApp(); exports.sendSampleNotification = functions.database.ref('/User/{userId}') .onWrite((change, context) => { const userData = change.after.val(); // 获取单个用户的数据 const userToken = userData.token; const userId = context.params.userId; if (!userToken) { console.log(`User ${userId} has no token, aborting push`); return null; } const message = { notification: { title: 'test title', body: 'test message' }, token: userToken }; // 返回推送的Promise,确保函数等待异步操作完成 return admin.messaging().send(message) .then((response) => { console.log("Message sent successfully:", response); return response; }) .catch((error) => { console.log("Error sending message: ", error); throw error; // 抛出错误让Firebase记录失败状态 }); });
额外注意事项
- 必须返回Promise:Cloud Functions需要知道异步操作什么时候完成,所以一定要返回推送相关的Promise,否则函数会在推送完成前就终止,导致推送失败。
- 检查Token有效性:一定要判断
token是否存在,避免因为某个用户没有Token导致整个推送流程出错。 - 查看数据结构:如果还是不确定,可以用
console.info(JSON.stringify(notificationSnapshot, null, 2))打印格式化后的数据集,这样能更清晰地看到数据的层级结构。
内容的提问来源于stack exchange,提问作者Yasuo
相关产品推荐
相关产品推荐

