Codelab:构建推送通知服务器

Kate Jeffreys
Kate Jeffreys

此 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 界面

  1. 点击 Remix to Edit 以使项目可修改。

设置身份验证

您需要先完成设置,然后才能让推送通知正常运行 向服务器和客户端授予身份验证密钥。 请参阅对 Web 推送协议请求进行签名 以了解具体原因。

  1. 点击 Tools ,然后点击 Terminal 以打开 Glitch 终端。
  2. 在终端中,运行 npx web-push generate-vapid-keys。复制私钥 和公钥值。
  3. 打开 .env 并更新 VAPID_PUBLIC_KEYVAPID_PRIVATE_KEY。设置 VAPID_SUBJECTmailto:test@test.test。所有这些值都应换行 。完成更新后,您的 .env 文件应如下所示: 类似于以下内容:
VAPID_PUBLIC_KEY="BKiwTvD9HA…"
VAPID_PRIVATE_KEY="4mXG9jBUaU…"
VAPID_SUBJECT="mailto:test@test.test"
  1. 关闭 Glitch 终端。
  1. 打开 public/index.js
  2. VAPID_PUBLIC_KEY_VALUE_HERE 替换为您的公钥的值。

管理订阅

您的客户端会处理大部分订阅流程。主要 你的服务器需要做的就是保存新的推送通知订阅 以及删除旧订阅。借助这些订阅,您可以 向客户端推送消息 请参阅为客户端订阅推送通知 了解有关订阅流程的更多背景信息。

保存新的订阅信息

  1. 如需预览网站,请按查看应用。然后按 全屏 全屏
  1. 点击应用标签页中的 Register Service Worker。在状态框中 应该会看到类似下面这样的消息:
Service worker registered. Scope: https://desert-cactus-sunset.glitch.me/
  1. 在应用标签页中,点击订阅推送。您的浏览器或操作系统很可能会 询问您是否允许该网站向您发送推送通知。点击允许(或 “浏览器/操作系统”使用的词组)。在状态框中,您应该看到类似如下的消息: 更改为:
Service worker subscribed to push.  Endpoint: https://fcm.googleapis.com/fcm/send/…
  1. 点击 Glitch 界面中的 View Source 以返回代码。
  2. 点击 Tools(工具),然后点击 Logs(日志)以打开 Glitch Logs。您 应该会看到 /add-subscription,后跟一些数据。“/add-subscription”现为 客户端发送的网址 发布 请求订阅推送通知。需要 下方是您需要保存的客户端订阅信息。
  3. 打开 server.js
  4. 使用以下代码更新 /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);
});

删除旧的订阅信息

  1. 返回应用标签页。
  2. 点击退订推送
  3. 再次查看故障日志。您应该会看到“/remove-subscription”已关注 由客户的订阅信息决定
  4. 使用以下代码更新 /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 推送协议请求) 归用户所用浏览器供应商所有。

  1. 使用以下代码更新 /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);
});
  1. 使用以下代码更新 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} `);
      });
  });
}
  1. 使用以下代码更新 /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);
  }
});
  1. 返回应用标签页。
  2. 点击退订推送,然后再次点击订阅推送。 如前所述,Glitch 会重启项目 并且项目配置为在启动时删除数据库。
  3. 点击通知我。您应该会收到一条推送通知。标题应 为 Hello, Notifications!,正文应为 ID: <ID>,其中 <ID> 为 随机数。
  4. 在其他浏览器或设备上打开您的应用,然后尝试为它们订阅推送通知 然后点击全部通知按钮。您应该会在 您订阅的所有设备(即推送通知正文中的 ID 保持不变)。

后续步骤

  • 阅读推送通知概览 以便从概念上更深入地了解推送通知的工作原理。
  • 查看 Codelab:构建推送通知客户端 学习如何构建一个客户端,用于请求通知权限、订阅 设备来接收推送通知,并使用 Service Worker 来接收 推送消息并将其显示为通知。