使用 onBackgroundMessage() 时,Firebase 网络推送通知被触发两次
Firebase web Push notifications is triggered twice when using onBackgroundMessage()
真不知道这些东西是怎么回事。
我通常为我的网站使用 FCM 网络推送。如果我在前台,我可以在网站上收到消息,如果我在后台,我可以收到通知。到目前为止一切顺利。
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-messaging.js');
firebase.initializeApp({
...
});
const messaging = firebase.messaging();
问题是 firebase-messaging-sw.js 的默认配置会在后台显示一个显示 chrome 图标的通知。我希望能够自定义此通知并显示我的应用程序图标。然后在网上阅读我发现我需要用onBackgroundMessage()拦截消息。
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-messaging.js');
firebase.initializeApp({
...
});
const messaging = firebase.messaging();
if (messaging) {
messaging.onBackgroundMessage(payload => {
const notificationTitle = payload.notification.title || payload.data.title;
const notificationOptions = {
body: payload.notification.body || payload.data.subtitle || '',
icon: 'https://firebasestorage.googleapis.com/v0/b/yes-4-web.appspot.com/o/pontonos%2Ficons%2Fandroid-chrome-192x192.png?alt=media&token=35616a6b-5e70-43a0-9284-d780793fa076',
data: payload.data
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(clients.matchAll({ type: "window" }).then(function(clientList) {
for (let i = 0; i < clientList.length; i++) {
const client = clientList[i];
if (client.url === '/' && 'focus' in client) {
if (event.notification.data.route) client.href(event.notification.data.route);
return client.focus();
}
}
if (clients.openWindow)
return clients.openWindow(event.notification.data.route || '/');
}));
});
}
问题是现在,当使用 onBackgroundMessage() 时,我会看到两个通知,带有 chrome 图标的原始消息和带有我的应用程序图标的个性化消息(见图)
另一个问题是,如果我点击原始通知,我的网站标签会成为主要焦点,但如果我点击个性化通知,我的网站会打开一个新标签。
这是预期的行为。
You can hide the notification, comment the show notification
messaging.onBackgroundMessage((payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = payload.notification.title||'';
const notificationOptions = {
body: payload.notification.body||'',
icon: '/firebase-logo.png'
};
// self.registration.showNotification(notificationTitle,
// notificationOptions);
self.registration.hideNotification();
});
可能来不及了。
我遇到了这个问题,我最终发现问题是我在我的有效负载中发送 'notification' 和 'data' 对象。
删除 'notification' 并仅保留 'data' 解决了问题。
来自 FCM 文档:
Use notification messages when you want FCM to handle displaying a notification on your client app's behalf. Use data messages when you want to process the messages on your client app.
FCM can send a notification message including an optional data payload. In such cases, FCM handles displaying the notification payload, and the client app handles the data payload.
这是我的有效负载现在的样子:
$payload = [
'message' => [
'token' => $token,
'data' => $msg,
//'notification' => $msg, (this caused the notification to deliver twice)
'webpush'=> [
'fcm_options'=> [
'link' => $link,
'analytics_label' => 'notification_label'
]
]
],
];
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
我有两次通知的同样问题。我不想使用 Firebase 控制台来测试您的通知。请改用 postman/insomnia。
您的通知被触发了两次,因为在 firebase 控制台中,他们发送数据通知并且只发送通知。这是我的 JSON 数据:
{
"to": "epasJ3fYJG3pjXC8UHCg37:APA91bHEbtPBtRzgEOKGR9Kg9DkZbHLdnMT2uEseQ8AzCSPKv8PkLsm_dkfN_AagFRcVhmOOqBlF9mNXtl7KqelG2g9tbeKTH1_Ey9KAGaNRGKwrIyttH58dP-jAWiGYKxLHu3mgdUMN",
/* here the problem. If you use both of them, you'll get notification twice. Just pick one. Notification key or data key */
"notification": {
"title": "Test title send from insomnia",
"body": "Test body send from insomnia",
"image": "https://mostrans.co.id/CompanyProfile/static/media/logo-mostrans.ff215158.png"
},
"data": {
"title": "Test title send from insomnia",
"body": "Test body send from insomnia",
"image": "https://mostrans.co.id/CompanyProfile/static/media/logo-mostrans.ff215158.png"
}
}
点击您的自定义通知,您可以这样做
const urlToOpen = new URL("/", self.location.origin).href;
const promiseChain = clients
.matchAll({
type: "window",
includeUncontrolled: true,
})
.then((windowClients) => {
let matchingClient = null;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
});
event.waitUntil(promiseChain);
阅读更多here
如果您有自定义通知,请不要在 POST 负载上使用 notification
。更改为 data
或其他关键字。
{
"to": "fH7CtR77FgxZsGiwbBwNT5:APA91bGSOeIW7o34lppBErHuCiapYOs5xrj1WOw1IR6gvn2TW3HsEGdyV5yfcyYksauhKCKXTWnbaULukPyJrH34Ht0GsonBt6_gYH9UdN_S3wR6w0ZDLSDo7iPbYO6Wbcyvw_9RTvi3",
"priority": "high",
"data": {
"title": "Title",
"body" : "First Notification",
"text": "Text"
}
}
function onBackgroundMessage() {
const messaging = firebase.messaging();
// [START messaging_on_background_message]
messaging.onBackgroundMessage((payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
const notification = payload.data; -----> here is custom
if (!notification) {
console.warn('[firebase-messaging-sw.js] Unknown notification on message ', payload);
return
}
// Customize notification here
const notificationOptions = {
...notification,
icon: '/img/icons/favicon-32x32.png'
};
self.registration.showNotification(
notification.title,
notificationOptions);
});
}
onBackgroundMessage();
如果您使用 Firebase Admin 发送消息,您可以 console.log 在 onBackgroundMessage 中,FCM 将发送一些数据如下所示。
有效负载包括 属性 notification
,来自 FCM Documentation
With FCM, you can send two types of messages to clients:
Notification messages, sometimes thought of as "display messages."
These are handled by the FCM SDK automatically.
意思是,如果你的数据有属性 notification
,FCM会自动处理,你不需要添加方法来显示onBackgroundMessage.
中的通知
如果您在 onBackgroundMessage 中添加显示通知的方法,预计会显示两次,因为第一个通知由FCM,第二个句柄由 onBackgroundMessage
.
如果您不希望 FCM 自动显示通知,您只需从负载中删除 属性 notification
。
你可以试试这个:
//Insert to firebase-messaging-sw.js
firebase.initializeApp(firebaseConfig);
class CustomPushEvent extends Event {
constructor(data) {
super('push');
Object.assign(this, data);
this.custom = true;
}
}
/*
* Overrides push notification data, to avoid having 'notification' key and firebase blocking
* the message handler from being called
*/
self.addEventListener('push', (e) => {
// Skip if event is our own custom event
if (e.custom) return;
// Kep old event data to override
const oldData = e.data;
// Create a new event to dispatch, pull values from notification key and put it in data key,
// and then remove notification key
const newEvent = new CustomPushEvent({
data: {
ehheh: oldData.json(),
json() {
const newData = oldData.json();
newData.data = {
...newData.data,
...newData.notification,
};
delete newData.notification;
return newData;
},
},
waitUntil: e.waitUntil.bind(e),
});
// Stop event propagation
e.stopImmediatePropagation();
// Dispatch the new wrapped event
dispatchEvent(newEvent);
});
上面的代码会将 属性 notification
中的键移动到 data
,因此预期结果将如下所示
这样,FCM 将不会显示通知,因为有效载荷没有 属性 notification
.
真不知道这些东西是怎么回事。 我通常为我的网站使用 FCM 网络推送。如果我在前台,我可以在网站上收到消息,如果我在后台,我可以收到通知。到目前为止一切顺利。
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-messaging.js');
firebase.initializeApp({
...
});
const messaging = firebase.messaging();
问题是 firebase-messaging-sw.js 的默认配置会在后台显示一个显示 chrome 图标的通知。我希望能够自定义此通知并显示我的应用程序图标。然后在网上阅读我发现我需要用onBackgroundMessage()拦截消息。
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-messaging.js');
firebase.initializeApp({
...
});
const messaging = firebase.messaging();
if (messaging) {
messaging.onBackgroundMessage(payload => {
const notificationTitle = payload.notification.title || payload.data.title;
const notificationOptions = {
body: payload.notification.body || payload.data.subtitle || '',
icon: 'https://firebasestorage.googleapis.com/v0/b/yes-4-web.appspot.com/o/pontonos%2Ficons%2Fandroid-chrome-192x192.png?alt=media&token=35616a6b-5e70-43a0-9284-d780793fa076',
data: payload.data
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(clients.matchAll({ type: "window" }).then(function(clientList) {
for (let i = 0; i < clientList.length; i++) {
const client = clientList[i];
if (client.url === '/' && 'focus' in client) {
if (event.notification.data.route) client.href(event.notification.data.route);
return client.focus();
}
}
if (clients.openWindow)
return clients.openWindow(event.notification.data.route || '/');
}));
});
}
问题是现在,当使用 onBackgroundMessage() 时,我会看到两个通知,带有 chrome 图标的原始消息和带有我的应用程序图标的个性化消息(见图)
另一个问题是,如果我点击原始通知,我的网站标签会成为主要焦点,但如果我点击个性化通知,我的网站会打开一个新标签。
这是预期的行为。
You can hide the notification, comment the show notification
messaging.onBackgroundMessage((payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = payload.notification.title||'';
const notificationOptions = {
body: payload.notification.body||'',
icon: '/firebase-logo.png'
};
// self.registration.showNotification(notificationTitle,
// notificationOptions);
self.registration.hideNotification();
});
可能来不及了。
我遇到了这个问题,我最终发现问题是我在我的有效负载中发送 'notification' 和 'data' 对象。
删除 'notification' 并仅保留 'data' 解决了问题。
来自 FCM 文档:
Use notification messages when you want FCM to handle displaying a notification on your client app's behalf. Use data messages when you want to process the messages on your client app.
FCM can send a notification message including an optional data payload. In such cases, FCM handles displaying the notification payload, and the client app handles the data payload.
这是我的有效负载现在的样子:
$payload = [
'message' => [
'token' => $token,
'data' => $msg,
//'notification' => $msg, (this caused the notification to deliver twice)
'webpush'=> [
'fcm_options'=> [
'link' => $link,
'analytics_label' => 'notification_label'
]
]
],
];
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
我有两次通知的同样问题。我不想使用 Firebase 控制台来测试您的通知。请改用 postman/insomnia。 您的通知被触发了两次,因为在 firebase 控制台中,他们发送数据通知并且只发送通知。这是我的 JSON 数据:
{
"to": "epasJ3fYJG3pjXC8UHCg37:APA91bHEbtPBtRzgEOKGR9Kg9DkZbHLdnMT2uEseQ8AzCSPKv8PkLsm_dkfN_AagFRcVhmOOqBlF9mNXtl7KqelG2g9tbeKTH1_Ey9KAGaNRGKwrIyttH58dP-jAWiGYKxLHu3mgdUMN",
/* here the problem. If you use both of them, you'll get notification twice. Just pick one. Notification key or data key */
"notification": {
"title": "Test title send from insomnia",
"body": "Test body send from insomnia",
"image": "https://mostrans.co.id/CompanyProfile/static/media/logo-mostrans.ff215158.png"
},
"data": {
"title": "Test title send from insomnia",
"body": "Test body send from insomnia",
"image": "https://mostrans.co.id/CompanyProfile/static/media/logo-mostrans.ff215158.png"
}
}
点击您的自定义通知,您可以这样做
const urlToOpen = new URL("/", self.location.origin).href;
const promiseChain = clients
.matchAll({
type: "window",
includeUncontrolled: true,
})
.then((windowClients) => {
let matchingClient = null;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
});
event.waitUntil(promiseChain);
阅读更多here
如果您有自定义通知,请不要在 POST 负载上使用 notification
。更改为 data
或其他关键字。
{
"to": "fH7CtR77FgxZsGiwbBwNT5:APA91bGSOeIW7o34lppBErHuCiapYOs5xrj1WOw1IR6gvn2TW3HsEGdyV5yfcyYksauhKCKXTWnbaULukPyJrH34Ht0GsonBt6_gYH9UdN_S3wR6w0ZDLSDo7iPbYO6Wbcyvw_9RTvi3",
"priority": "high",
"data": {
"title": "Title",
"body" : "First Notification",
"text": "Text"
}
}
function onBackgroundMessage() {
const messaging = firebase.messaging();
// [START messaging_on_background_message]
messaging.onBackgroundMessage((payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
const notification = payload.data; -----> here is custom
if (!notification) {
console.warn('[firebase-messaging-sw.js] Unknown notification on message ', payload);
return
}
// Customize notification here
const notificationOptions = {
...notification,
icon: '/img/icons/favicon-32x32.png'
};
self.registration.showNotification(
notification.title,
notificationOptions);
});
}
onBackgroundMessage();
如果您使用 Firebase Admin 发送消息,您可以 console.log 在 onBackgroundMessage 中,FCM 将发送一些数据如下所示。
有效负载包括 属性 notification
,来自 FCM Documentation
With FCM, you can send two types of messages to clients:
Notification messages, sometimes thought of as "display messages." These are handled by the FCM SDK automatically.
意思是,如果你的数据有属性 notification
,FCM会自动处理,你不需要添加方法来显示onBackgroundMessage.
如果您在 onBackgroundMessage 中添加显示通知的方法,预计会显示两次,因为第一个通知由FCM,第二个句柄由 onBackgroundMessage
.
如果您不希望 FCM 自动显示通知,您只需从负载中删除 属性 notification
。
你可以试试这个:
//Insert to firebase-messaging-sw.js
firebase.initializeApp(firebaseConfig);
class CustomPushEvent extends Event {
constructor(data) {
super('push');
Object.assign(this, data);
this.custom = true;
}
}
/*
* Overrides push notification data, to avoid having 'notification' key and firebase blocking
* the message handler from being called
*/
self.addEventListener('push', (e) => {
// Skip if event is our own custom event
if (e.custom) return;
// Kep old event data to override
const oldData = e.data;
// Create a new event to dispatch, pull values from notification key and put it in data key,
// and then remove notification key
const newEvent = new CustomPushEvent({
data: {
ehheh: oldData.json(),
json() {
const newData = oldData.json();
newData.data = {
...newData.data,
...newData.notification,
};
delete newData.notification;
return newData;
},
},
waitUntil: e.waitUntil.bind(e),
});
// Stop event propagation
e.stopImmediatePropagation();
// Dispatch the new wrapped event
dispatchEvent(newEvent);
});
上面的代码会将 属性 notification
中的键移动到 data
,因此预期结果将如下所示
这样,FCM 将不会显示通知,因为有效载荷没有 属性 notification
.