在Web应用程序中发送通知的方法及教程

了解如何使用JavaScript在您的网站或Web应用程序中发送通知。本文介绍了使用服务工作者、通知API和推送API发送本地通知和推送通知的步骤。

有没有想过一些网页、网址或您在浏览器上使用的其他喜爱应用程序如何发送通知给您?这并不是魔法,有一些方法可以做到,本文将介绍其中一种方法。

在Web开发世界中,通过引入一些网页API,现在更容易完成一些可能需要使用移动应用程序才能实现的任务之一,即发送通知。使用通知API和推送API结合服务工作者,在Web浏览器上发送通知变得比以往任何时候都更简单。

本文将解释如何在您的网站或Web应用程序中发送通知,并使用JavaScript进行演示。

通知可以分为两种类型:

  • 本地通知:由应用程序自己生成。

  • 推送通知:由服务器(后端)通过推送事件生成的通知。例如,您的最喜欢的YouTuber刚刚发布了一部视频,您即使不在YouTube Web应用程序上,也会在获取网络连接后获得通知。

本文将在文章中详细介绍每一种类型的通知。在开始之前,我们需要了解哪些内容?

发送通知所需的组件:

  • 服务工作者:服务工作者实际上充当Web应用程序、浏览器和网络(当网络可用时)之间的代理服务器。它们的目的是为了实现有效的离线体验,拦截网络请求,并根据网络的可用性采取适当的行动。它们还将允许访问推送通知和后台同步API。服务工作者的一个好例子是当您离线时,仍然可以从YouTube通知服务器向您的浏览器发送通知,但直到您重新连接到互联网时,您才会收到通知。

  • 通知API:该API用于像移动设备一样在您的Web应用程序中向用户显示通知提示,例如当用户在您的应用程序中点击按钮或执行操作时。

  • 推送API:该API用于从服务器获取推送消息。

发送本地通知

在发送本地通知到您的Web应用程序中,我们将采取以下3个步骤:

  • 我们需要使用requestPermission方法通过通知API来请求用户发送通知的权限。

  • 如果权限被授予,我们的服务工作者现在会监听推送事件。当推送事件到达时,服务工作者将从消息中提取信息并使用通知API显示通知。

  • 如果权限未被授予,您还应正确处理该情况的代码。

让我们首先编写一个简单页面的示例代码,在单击“提醒我”按钮时实现本地通知的功能。

HTML代码

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>示例</title>
</head>
<body>
    <button class="notify">提醒我</button>
</body>
<script src="./script.js"></script>
</html>

Javascript 代码 script.js

const Notifybtn = document.querySelector(".notify");
const sendNotification = ()=>{
    if(!("Notification" in window)){
        throw new Error("您的浏览器不支持推送通知");
    }
    Notification.requestPermission().then((Permission)=>{
        const notificationOptions = {
            body:"欢迎使用 JavaScript 推送通知",
            icon:"./image.png"
        }
        new Notification("推送通知",notificationOptions);
    })
};
Notifybtn.addEventListener("click", sendNotification);

在上面的代码中,我们有 sendNotification 函数,我们首先检查用户的浏览器是否支持通知,然后我们请求用户允许发送通知,如果没有授予权限,我们将无法发送通知。
为了发送通知,我们使用通知构造函数,将通知标题作为第一个参数,将通知选项作为第二个参数。运行这个你会得到这样的东西

基本本地通知

让我们稍微加强一下,我们想要发送更具交互性的通知,用户可以点击按钮或我们想要进行的任何类型的交互。让我们尝试修改我们的代码来满足这种需求。

const sendNotification = ()=>{
    if(!("Notification" in window)){
        throw new Error("您的浏览器不支持推送通知");
    }
    Notification.requestPermission().then((Permission)=>{
        const notificationOptions = {
            body:"欢迎使用 JavaScript 推送通知",
            icon:"./image.png",
            actions: [
      {
        action: "thanks",
        title: "Thanks",
      },
      {
        action: "view_profile",
        title: "View Profile",
      },
    ],
        }
        new Notification("推送通知",notificationOptions);
    })
};

在这里,我们向通知选项添加了 actions 属性,用户可以在通知上有按钮进行交互。当您运行此命令时,您会收到如下错误:

当我们尝试使用通知构造函数创建可交互操作时出现错误

相当明确的错误消息,仅在使用服务工作者时才支持操作,我们该怎么做?

使用 Service Worker 发送通知

首先,我们需要有 Service Worker 文件并注册 Service Worker。让我们的 serviceWorker.js 文件中只有一个简单的 console.log 。

//serviceWorker.js
console.log("您好,欢迎来到服务人员")

我们现在可以继续注册我们的 Service Worker。

const registerServiceWorker = async () => {
  if ("serviceWorker" in navigator) {
    try {
      const registration = await navigator.serviceWorker.register(
        "serviceWorker.js",
        {
          scope: "./",
        }
      );
      return registration;
    } catch (error) {
      console.error(`注册失败 ${error}`);
    }
  }
};

因此,是时候通过修改我们的 sendNotification 函数来创建具有交互操作的通知

// script.js
const sendNotification = async () => {
  let notificationOptions = {
    body: "Toy模板网向您发送了好友请求",
    icon: "./image.png",
   data: {
      requestId: "1234",
      username: "elonmusk"
    },
    actions: [
      {
        action: "accept",
        title: "Accept",
      },
      {
        action: "view_profile",
        title: "View Profile",
      },
    ],
    tag: "friend_request",
  };
  const sw = await registerServiceWorker();
  sw.showNotification("Friend Request", notificationOptions);
};

在这里你有它

当您单击时,似乎没有发生任何事情:) 让我们进一步监听 serviceWorker 中的 notificationclick 事件。

// serviceWorker.js

self.addEventListener("notificationclick", (event) => {
  event.notification.close();

  switch (event.notification.tag) {
  case "friend_request":{
    switch (event.action) {
      case "accept": {
        console.log("accept request API call.. with ", event.notification.data);
      }

      case "view_profile":
        {
          //直接进入个人资料页面
          event.waitUntil(
            clients
              .matchAll({
                type: "window",
                includeUncontrolled: true,
              })
              .then((windowClients) => {
                const matchingClient = windowClients.find(
                  (wc) => wc.url === urlToOpen
                );

                if (matchingClient) {
                  return matchingClient.focus();
                } else {
                  return clients.openWindow(event.notification.data.username);
                }
              })
          );
        }

        break;
      // 处理其他动作...
    }
  }
  }
});

在这里您可以看到我们有两个操作“接受请求”和“查看个人资料”,当通知的标签为“friend_request”时我们会执行该操作,并且我们基本上是在通知中传递数据,这是需要注意的重要事项。

发送推送通知

到目前为止,我们已经能够在我们的网络应用程序中发送本地通知,这很酷,对吧?但大多数时候,在构建实际应用程序时,您需要的是推送通知;从服务器生成的通知。在本节中,我们将在 ExpressJS 中建立后端服务器。让我们设置我们的服务器。

// app.js

const express = require("express");
const cors = require("cors");
require("dotenv").config();

const { API_PREFIX } = process.env;
const app = express();
const { models } = require("./config/db");
const { sendNotification } = require("./utils/helper");
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(
  cors({
    origin: "*",
  })
);
app.disable("x-powered-by");
module.exports = app
// index.js
const app = require("./app");
const { PORT } = process.env;
const connection = require("./config/db");
(async () => {
    await connection
      .sync()
      .then(() => {
        console.log("Database successfully connected");
      })
      .catch((err) => {
        console.log(err.message);
        return process.exit(1);
      });
    app.listen(PORT, () => {
        console.log(
          `<<<<<<<<<<<<<<<< Yeeeep,Server running on port ${PORT}..>>>>>>>>>>>`
        );
    });
  })();

这样,我们的后端服务器就启动并运行了(PS:由于某些导入,此代码片段将无法正常工作,在本文末尾,您可以找到所有代码的 GitHub 存储库的链接:文章结尾”

让我们再次回退到前端...为了能够发送推送通知,我们需要以下内容:

  • VAPID 键

  • Web 应用程序必须通过 Service Worker 订阅推送服务

VAPID:Web Push 的自愿应用程序服务器标识是一个允许后端服务器向推送服务(浏览器特定服务)标识自身的规范。这是一种安全措施,可防止其他人向应用程序的用户发送消息。

APID 密钥如何确保安全?
简而言之:
您在应用程序服务器上生成一组私钥和公钥(VAPID 密钥)。
当前端应用程序尝试订阅推送服务时,公钥将被发送到推送服务。现在推送服务知道来自您的应用程序服务器(后端)的公钥。subscribe 方法将为您的前端 Web 应用程序返回一个唯一的端点。您将将此唯一端点存储在后端应用程序服务器上。
从应用程序服务器,您将向刚刚保存的唯一推送服务端点发出 API 请求。在此 API 请求中,您将使用私钥对 Authorization 标头中的一些信息进行签名。这允许推送服务验证请求是否来自正确的应用程序服务器。
验证标头后,推送服务会将消息发送到前端 Web 应用程序。

让我们继续生成 VAPID 密钥,我们需要在您的后端或全局安装“web-push”dendency...

npm i web-push

安装后就可以运行

npx web-push generate-vapid-keys

你有这样的东西

//serviceWorker.js
const urlB64ToUint8Array = (base64String) => {
  const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding)
    .replace(/\-/g, "+")
    .replace(/_/g, "/");
  const rawData = atob(base64);
  const outputArray = new Uint8Array(rawData.length);
  for (let i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
};

self.addEventListener("activate", async () => {
  try {
    const applicationServerKey = urlB64ToUint8Array(
      "<YOUR_PUBLIC_KEY>"
    );
    const options = { applicationServerKey, userVisibleOnly: true };
    const subscription = await self.registration.pushManager.subscribe(options);
    console.log(JSON.stringify(subscription))
  } catch (err) {
    console.log("Error", err);
  }
});

在这里,我们监听 Service Worker 注册和激活的时间,我们使用 VAPID 公钥,将其从 Base64 字符串转换为订阅选项所需的数组缓冲区,并且我们还在属性中添加了 userVisibleOnly设置为 true。您需要始终将 userVisibleOnly 发送为 true 。该参数限制开发者使用推送消息进行通知。也就是说,开发人员将无法使用推送消息在不显示通知的情况下静默向服务器发送数据。目前,它必须设置为 true,否则您会收到权限被拒绝的错误。本质上,出于隐私和安全考虑,目前不支持静默推送消息。

当它成功运行时,你会看到如下内容:

{
"endpoint":"https://fcm.googleapis.com/fcm/send/<some_strange_id>",
"expirationTime":null,
"keys":{"p256dh":"<some_key>","auth":"<some_id>"}
}

我们得到的响应应该保存在后端的数据库中,因此让我们在后端创建两个端点,一个用于保存订阅,另一个用于发送通知。

//app.jsapp.post(`/${API_PREFIX}save-subscription`, async (req, res) => {

  try {
    const subscription = JSON.stringify(req.body);
    console.log(subscription);
    const sub = await models.subscriptions.create({ subsription:subscription });
    res.status(201).json({ message: "Subscription Successful" });
  } catch (error) {
    res.status(500).json({ message: error.message });
  }});app.post(`/${API_PREFIX}send-notification`, async (req, res) => {
  try {
    const { id } = req.body;
    const sub = await models.subscriptions.findOne({where:{id}});
    const message = {
      body: "Elon Musk sent you a friend request",
      icon: "https://media.npr.org/assets/img/2022/06/01/ap22146727679490-6b4aeaa7fd9c9b23d41bbdf9711ba54ba1e7b3ae-s800-c85.webp",
      data: {
        requestId: "1234",
        username: "elonmusk"
      },
      actions: [
        {
          action: "accept",
          title: "Accept",
        },
        {
          action: "view_profile",
          title: "View Profile",
        },
      ],
      tag: "friend_request",
    };
    await sendNotification(sub.subsription, message);
    res.json({ message: "message sent" });    
  } catch (error) {
    res.status(500).json({ message: error.message });

  }});

我们还需要创建有助于发送推送通知的导入实用函数。

// utils/helper.js
const webpush = require("web-push");
const { VAPID_PRIVATE_KEY, VAPID_PUBLIC_KEY } = process.env;
//setting our previously generated VAPID keys
webpush.setVapidDetails(
  "mailto:<your_email>",
  VAPID_PUBLIC_KEY,
  VAPID_PRIVATE_KEY
);
//function to send the notification to the subscribed device
const sendNotification = async (subscription, dataToSend) => {
  try {
   await webpush.sendNotification(subscription, JSON.stringify(dataToSend)); //string or Node Buffer
  } catch (error) {
    console.log(error);
    throw new Error(error.message);
  }
};
module.exports = { sendNotification };

我们有一个端点来保存订阅,还有另一个端点来发送通知(PS:这只是一个示例/演示项目,在现实生活中,您很可能不会有一个端点来发送通知,而是一个实用程序服务(发生某些操作时可以触发的类或函数)。
现在让我们回到前端来同步事物,我们首先通过调用端点将推送服务订阅保存到数据库。

// serviceWorker.js
const saveSubscription = async (subscription) => {
  const SERVER_URL = "http://localhost:5005/api/save-subscription";
  const response = await fetch(SERVER_URL, {
    method: "post",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(subscription),
  });
  return response.json();
};
self.addEventListener("activate", async () => {
  try {
    const applicationServerKey = urlB64ToUint8Array(
      "<YOUR_VAPID_PUBLIC_KEY>"
    );
    const options = { applicationServerKey, userVisibleOnly: true };
    const subscription = await self.registration.pushManager.subscribe(options);
    const response = await saveSubscription(subscription);
  } catch (err) {
    console.log("Error", err);
  }
});

我们就差几步了!!现在让我们使用 showNotification 函数来显示收到的推送通知

// serviceWorker.jsconst showLocalNotification = (title, data, swRegistration) => {
  swRegistration.showNotification(title, data);};

该函数接受三个参数;推送通知的标题、notificationOption 和 Service Worker 注册。最后,当发出推送通知时,我们需要监听推送事件。

// serviceWorker.js
self.addEventListener("push", function (event) {
  if (event.data) {
    console.log("Push event!! ", JSON.parse(event.data.text()));
    showLocalNotification(
      "Notification ",
      JSON.parse(event.data.text()),
      self.registration
    );
  } else {
    console.log("Push event but no data");
  }
});

看看我们有什么:

后台发送推送通知

我们还必须注意,当页面或浏览器刷新/重新启动时,Service Worker 仍然保持注册状态,并且您可能需要更改 Service Worker 文件中的某些内容,您可以调用此函数

const unregisterServiceWorkers = ()=>{
  if (window.navigator && navigator.serviceWorker) {
    navigator.serviceWorker.getRegistrations().then(function (registrations) {
      for (let registration of registrations) {
        registration.unregister();
      }
    });
  }
}

每当我们使用数据库生成的 ID 访问发送通知端点时,我们都会在您的设备上收到推送通知,这不是很酷吗?如果您因为推送通知而希望构建一个移动应用程序,那么如果没有“权衡”,您可以考虑构建一个网络应用程序......

本文的代码示例(https://github.com/oluwatobiisaiah/web-notification-simplified)文章来源地址https://www.toymoban.com/diary/web/460.html

到此这篇关于在Web应用程序中发送通知的方法及教程的文章就介绍到这了,更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

原文地址:https://www.toymoban.com/diary/web/460.html

如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用
使用.NET Core 7构建一个简洁的RESTful Minimal API
上一篇 2023年10月24日 00:47
GraalVM 的特性和未来发现前景
下一篇 2023年10月24日 01:37

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包