此 Codelab 将逐步介绍如何构建推送通知服务器。 完成此 Codelab 后,您将拥有一个具备以下功能的服务器:
- 跟踪推送通知订阅(即服务器创建 以及当客户端选择接收推送通知时,数据库记录会 在客户端退出时删除现有的数据库记录)
- 向单个客户端发送推送通知
- 向所有订阅的客户端发送推送通知
此 Codelab 主要是帮助您在实践中学习 结账 推送通知的工作原理 以了解推送通知概念。
此 Codelab 的客户端代码已完成。您只会 实现服务器的方法。要了解如何实现 推送通知客户端,请参阅Codelab:构建推送通知 客户端。
请查看 push-notifications-server-codelab-complete (来源) 查看完整代码。
浏览器兼容性
此 Codelab 已知可与下列操作系统和浏览器组合配合使用:
- Windows:Chrome、Edge
- macOS:Chrome、Firefox
- Android:Chrome、Firefox
此 Codelab 已知不适合以下操作系统 (或操作系统和浏览器组合):
- macOS:Brave、Edge、Safari
- iOS
应用堆栈
- 服务器是基于 Express.js 构建的。
- web-push Node.js 库 负责处理所有推送通知逻辑。
- 使用 lowdb 将订阅数据写入 JSON 文件。
您无需使用上述任何技术来实现推送通知。 我们之所以选择这些技术,是因为它们能够提供可靠的 Codelab 体验。
设置
获取代码的可修改副本
在这些说明右侧显示的代码编辑器将称为 Glitch 界面。
- 点击 Remix to Edit 以使项目可修改。
设置身份验证
您需要先完成设置,然后才能让推送通知正常运行 向服务器和客户端授予身份验证密钥。 请参阅对 Web 推送协议请求进行签名 以了解具体原因。
- 点击 Tools ,然后点击 Terminal 以打开 Glitch 终端。
- 在终端中,运行
npx web-push generate-vapid-keys
。复制私钥 和公钥值。 - 打开
.env
并更新VAPID_PUBLIC_KEY
和VAPID_PRIVATE_KEY
。设置VAPID_SUBJECT
至mailto:test@test.test
。所有这些值都应换行 。完成更新后,您的.env
文件应如下所示: 类似于以下内容:
VAPID_PUBLIC_KEY="BKiwTvD9HA…"
VAPID_PRIVATE_KEY="4mXG9jBUaU…"
VAPID_SUBJECT="mailto:test@test.test"
- 关闭 Glitch 终端。
- 打开
public/index.js
。 - 将
VAPID_PUBLIC_KEY_VALUE_HERE
替换为您的公钥的值。
管理订阅
您的客户端会处理大部分订阅流程。主要 你的服务器需要做的就是保存新的推送通知订阅 以及删除旧订阅。借助这些订阅,您可以 向客户端推送消息 请参阅为客户端订阅推送通知 了解有关订阅流程的更多背景信息。
保存新的订阅信息
- 如需预览网站,请按查看应用。然后按 全屏 。
- 点击应用标签页中的 Register Service Worker。在状态框中 应该会看到类似下面这样的消息:
Service worker registered. Scope: https://desert-cactus-sunset.glitch.me/
- 在应用标签页中,点击订阅推送。您的浏览器或操作系统很可能会 询问您是否允许该网站向您发送推送通知。点击允许(或 “浏览器/操作系统”使用的词组)。在状态框中,您应该看到类似如下的消息: 更改为:
Service worker subscribed to push. Endpoint: https://fcm.googleapis.com/fcm/send/…
- 点击 Glitch 界面中的 View Source 以返回代码。
- 点击 Tools(工具),然后点击 Logs(日志)以打开 Glitch Logs。您
应该会看到
/add-subscription
,后跟一些数据。“/add-subscription
”现为 客户端发送的网址 发布 请求订阅推送通知。需要 下方是您需要保存的客户端订阅信息。 - 打开
server.js
。 - 使用以下代码更新
/add-subscription
路由处理程序逻辑:
app.post('/add-subscription', (request, response) => {
console.log('/add-subscription');
console.log(request.body);
console.log(`Subscribing ${request.body.endpoint}`);
db.get('subscriptions')
.push(request.body)
.write();
response.sendStatus(200);
});
删除旧的订阅信息
- 返回应用标签页。
- 点击退订推送。
- 再次查看故障日志。您应该会看到“
/remove-subscription
”已关注 由客户的订阅信息决定 - 使用以下代码更新
/remove-subscription
路由处理程序逻辑:
app.post('/remove-subscription', (request, response) => {
console.log('/remove-subscription');
console.log(request.body);
console.log(`Unsubscribing ${request.body.endpoint}`);
db.get('subscriptions')
.remove({endpoint: request.body.endpoint})
.write();
response.sendStatus(200);
});
发送通知
如发送推送消息中所述, 实际上,您的服务器并不会将推送消息直接发送到客户端。 相反,它依靠推送服务来完成此操作。您的服务器基本上 而只是启动向客户端推送消息的过程, 向 Web 服务(推送服务)发出的服务请求(Web 推送协议请求) 归用户所用浏览器供应商所有。
- 使用以下代码更新
/notify-me
路由处理程序逻辑:
app.post('/notify-me', (request, response) => {
console.log('/notify-me');
console.log(request.body);
console.log(`Notifying ${request.body.endpoint}`);
const subscription =
db.get('subscriptions').find({endpoint: request.body.endpoint}).value();
sendNotifications([subscription]);
response.sendStatus(200);
});
- 使用以下代码更新
sendNotifications()
函数:
function sendNotifications(subscriptions) {
// TODO
// Create the notification content.
const notification = JSON.stringify({
title: "Hello, Notifications!",
options: {
body: `ID: ${Math.floor(Math.random() * 100)}`
}
});
// Customize how the push service should attempt to deliver the push message.
// And provide authentication information.
const options = {
TTL: 10000,
vapidDetails: vapidDetails
};
// Send a push message to each client specified in the subscriptions array.
subscriptions.forEach(subscription => {
const endpoint = subscription.endpoint;
const id = endpoint.substr((endpoint.length - 8), endpoint.length);
webpush.sendNotification(subscription, notification, options)
.then(result => {
console.log(`Endpoint ID: ${id}`);
console.log(`Result: ${result.statusCode}`);
})
.catch(error => {
console.log(`Endpoint ID: ${id}`);
console.log(`Error: ${error} `);
});
});
}
- 使用以下代码更新
/notify-all
路由处理程序逻辑:
app.post('/notify-all', (request, response) => {
console.log('/notify-all');
response.sendStatus(200);
console.log('Notifying all subscribers');
const subscriptions =
db.get('subscriptions').cloneDeep().value();
if (subscriptions.length > 0) {
sendNotifications(subscriptions);
response.sendStatus(200);
} else {
response.sendStatus(409);
}
});
- 返回应用标签页。
- 点击退订推送,然后再次点击订阅推送。 如前所述,Glitch 会重启项目 并且项目配置为在启动时删除数据库。
- 点击通知我。您应该会收到一条推送通知。标题应
为
Hello, Notifications!
,正文应为ID: <ID>
,其中<ID>
为 随机数。 - 在其他浏览器或设备上打开您的应用,然后尝试为它们订阅推送通知 然后点击全部通知按钮。您应该会在 您订阅的所有设备(即推送通知正文中的 ID 保持不变)。
后续步骤
- 阅读推送通知概览 以便从概念上更深入地了解推送通知的工作原理。
- 查看 Codelab:构建推送通知客户端 学习如何构建一个客户端,用于请求通知权限、订阅 设备来接收推送通知,并使用 Service Worker 来接收 推送消息并将其显示为通知。