如何通过 Node.js 上的推送通知实现一些复杂的行为
Posted
技术标签:
【中文标题】如何通过 Node.js 上的推送通知实现一些复杂的行为【英文标题】:How to implement some sophisticated behavior, with push-notifications on Node.js 【发布时间】:2018-06-03 09:05:39 【问题描述】:我正在试验从 Node.js 应用程序发送的推送通知。 通过一些教程和示例,我现在有了一个可以开始工作的迷你应用程序。
它的作用非常基本,当它被加载到浏览器中时,会触发一个通知,并且用户会看到一条消息弹出。
它基本上由四个文件组成: index.js、index.html、worker.js 和 client.js。
作为第一个实验,我想实现一些稍微复杂的行为。
应用应在启动时触发 A 类通知(正如它已经在做的那样),然后每 121 分钟触发一次 B 类通知。
这种事情是可能的还是不可能的?
如果可以,我该怎么做?
作为参考,我在这里放了两个相关文件:
index.js:
const express = require('express'),
webPush = require('web-push'),
bodyParser = require('body-parser'),
path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'client')));
app.use(bodyParser.json());
const privateVapIdKey = process.env.privVapIdKey,
publicVapIdKey = process.env.pubVapIdKey;
webPush.setVapidDetails(
'mailto:myemail@example.com',
publicVapIdKey,privateVapIdKey);
// Subscribe Route.
app.post('/subscribe',(req,res) =>
const subscription = req.body; // Get Push Subscription Object.
res.status(201).json(); // Send 201. Resource created.
// Do a lot of useful things ......
.......
// Create the PayLoad.
const payload = JSON.stringify(
title:'A big title!',
........
);
// Pass Object to sendNotification.
webPush.sendNotification(subscription,payload).catch(err => console.error(err));
);
const port = 5003;
const PORT = process.env.PORT || port;
app.listen(PORT, () => console.log(`Listening on $ PORT `));
client.js:
const publicVapIdKey = 'my-secret-3453754...pubVapIdKey';
// Chec for ServiceWorker.
if ('serviceWorker' in navigator)
send().catch(err => console.error(err));
// Register ServiceWorker, Register Push, Send Push.
async function send()
console.log("Registering ServiceWorker.");
const register = await navigator.serviceWorker.register('/worker.js',
scope: "/"
);
console.log('ServiceWorker registered.');
console.log("Registering Push.");
//register.pushManager.uns
const subscription = await register.pushManager.subscribe(
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicVapIdKey)
);
console.log('Push registered.');
console.log("Sending Push.");
await fetch('/subscribe',
method: 'POST',
body: JSON.stringify(subscription),
headers:
'content-type': 'application/json'
);
console.log('Push sent.');
function urlBase64ToUint8Array(base64String)
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i)
outputArray[i] = rawData.charCodeAt(i);
return outputArray;
【问题讨论】:
【参考方案1】:使用 setTimeout 和 setInterval 可以实现简单的事件计时:https://nodejs.org/api/timers.html 它们的工作方式与浏览器端 javascript 相同。
如需更高级的内容,请尝试node-cron
【讨论】:
我试图用nodejs.org/api/timers.html 做点什么。问题是我无法使用 setImmediate 或 setTimeout 循环来重复操作。我一定是错过了什么。 您正在寻找setInterval
。或者,您可以通过在回调中设置新的超时来递归调用setTimeout
。
是的,我确实使用setInterval
做了一些工作,由于某种原因,我之前尝试过的对setTimeout
的递归调用不起作用。【参考方案2】:
为了能够向端点发送通知,您需要订阅它,因此我们必须存储客户端订阅:
const subscriptions = [];
app.post('/subscribe',(req,res) =>
const subscription = req.body;
// FIXME: Validate subscription!!!
subscriptions.push(subscription);
res.status(201).json();
webPush.sendNotification(subscription, title: "A" );
);
现在每 121 分钟,我们会检查所有订阅并传递我们的消息 B:
const MINS = 60 * 1000;
setInterval(() =>
for(const subscription of subscriptions)
webPush.sendNotification(subscription, title: "B" );
, 121 * MINS);
PS:您可能还应该添加一个“取消订阅”端点,否则您可能会在某些时候将通知发送到死端点
【讨论】:
取得一些进展后,我也觉得我需要一些“取消订阅”端点。我该如何设置这样的东西?【参考方案3】:有可能!但我建议您将Admin FCM 用于服务器端,它比 web-push 更新了最新的库,并且更容易推送通知。 p>
//node.js serverside code
const FCM = require("firebase-admin");
//fcm-push-notification.json is where all the configurations are
const serviceAccount = require("fcm-push-notification.json");
FCM.initializeApp(
credential: SERVICE_ACCOUNT,
databaseURL: DBURL
);
// In your Subscribe Route.
app.post('/subscribe',(req,res) =>
FCM.messaging()
.sendToDevice(
DEVICE_TOKEN,
data: DATA,
notification:
title: "A big title!",
body: "HELLO PUSH!"
)
.then(res =>
// do something here
)
);
这里是服务人员
// put firebase-messaging-sw.js service worker
// this is to get notified in the background when the tab is closed on not active
(global =>
importScripts("https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js");
importScripts(
"https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js"
);
firebase.initializeApp(
messagingSenderId: SENDER_ID
);
const messaging = firebase.messaging();
console.log("Service Worker started!");
messaging.setBackgroundMessageHandler(payload =>
console.log("Message received In background");
// Customize notification here
const notificationOptions =
body: "Background Message body.",
icon: "/firebase-logo.png"
;
return global.registration.showNotification("TITLE", notificationOptions);
);
)(self);
在你的 javascript 中
//to get notified in forground just do this
import firebase from "firebase";
firebase.initializeApp(FCM_CONF);
let messaging = firebase.messaging();
messaging.usePublicVapidKey(VAPID_KEY);
messaging.onMessage(payload =>
console.log("Message received from foreground ");
);
最后创建一个 manifest.json
//create manifest.json with content like this
"gcm_sender_id": SENDER_ID
每 121 分钟触发一次 B 类通知。使用类似later.js
var later = require('later');
var schedule = later.parse.text('every 121 min');
var timer = later.setTimeout(() =>
// fired every 121 minutes
FCM.messaging()
.sendToDevice(
DEVICE_TOKEN,
data: DATA,
notification:
title: "A big title!",
body: "HELLO PUSH!"
)
, schedule);
【讨论】:
以上是关于如何通过 Node.js 上的推送通知实现一些复杂的行为的主要内容,如果未能解决你的问题,请参考以下文章