如何使用 Server-Sent Events 和 Safari 推送 Apple 推送提示
前言
Server-Sent Events (SSE) 是一种 Web API,它允许浏览器和服务器之间建立一种持久化的连接,从而能够实现服务器实时推送数据到浏览器。SSE 提供的数据流是单向的,只能由服务器向浏览器推送数据。SSE 的优势是可以在浏览器中使用标准的 XMLHttpRequest 对象从服务器获取数据,而不需要使用 WebSocket 进行通讯。
Safari 推送是苹果推出的一种基于 Web 推送通知的服务,它使得网站可以在用户离开网站后向其推送通知消息。苹果提供了 Web Push Notification API,使得开发者可以在 Safari 浏览器中实现推送通知功能。
在本文中,我们将结合 Server-Sent Events 和 Safari 推送,介绍如何实现基于 SSE 的 Safari 推送服务。
步骤
实现 SSE 服务
首先,我们需要在服务器端实现 SSE 服务。下面是一个基本的 SSE 服务器的实现示例(使用 Node.js):
const http = require('http'); http.createServer((req, res) => { // 设置响应头,指定要发送的数据格式和编码 res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Charset': 'utf-8', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // 发送数据 setInterval(() => { // 构造 SSE 数据格式 const data = { event: 'event-name', data: JSON.stringify({ message: 'Hello, world!' }) }; res.write(`data: ${data.data}\nevent: ${data.event}\n\n`); }, 1000); // 关闭连接 req.connection.addListener("close", () => { res.end(); }, false); }).listen(8000);
上述代码创建了一个 HTTP 服务器,并通过 res.writeHead
方法设置了响应头,指定了要发送的数据格式和编码等。接下来,我们定义了一个发送数据的定时器,并在定时器中构造了 SSE 数据格式进行发送。其中,事件名称和消息体都是自定义的。最后,通过 req.connection.addListener
方法实现了在浏览器窗口关闭时,关闭 SSE 服务器与浏览器的连接。
在浏览器中实现 SSE 客户端
SSE 的客户端实现比较简单,下面是一个基本的 SSE 客户端的实现示例:
const eventSource = new EventSource('http://localhost:8000/'); eventSource.onmessage = function(event) { console.log(JSON.parse(event.data)); }; eventSource.addEventListener('event-name', function(event) { console.log(JSON.parse(event.data)); });
上述代码通过创建一个 EventSource
对象,指定 SSE 服务器的地址来创建 SSE 客户端。接下来,我们可以通过 onmessage
和 addEventListener
方法分别处理服务器推送过来的消息。
实现 Safari 推送
通过 SSE 实现了实时向浏览器推送数据后,下一步就是将其转化为 Safari 推送通知。
Safari 推送通知需要浏览器支持 Web Push Notification API 来实现。由于最新版本的 Safari 浏览器已经支持 Web Push Notification API,因此我们只需要使用该 API 来生成通知消息即可。
下面是一个基本的 Safari 推送服务的实现示例:
const webPush = require('web-push'); const express = require('express'); const bodyParser = require('body-parser'); const cors = require('cors'); const app = express(); const whitelist = ['https://www.example.com']; const corsOptions = { origin: function(origin, callback) { if (whitelist.indexOf(origin) !== -1) { callback(null, true) } else { callback(new Error('Not allowed by CORS')) } } }; app.use(cors(corsOptions)); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); // 初始化 Push 服务 webPush.setVapidDetails( 'mailto:your-email@example.com', 'publicKey', 'privateKey' ); // 接收 SSE 服务器推送来的消息 app.post('/push', (req, res) => { // 接收 SSE 服务器推送过来的消息体 const message = req.body.message; const payload = JSON.stringify({ title: 'Notification Title', message: message }); // 向浏览器推送通知消息 webPush.sendNotification(req.body.subscription, payload) .then(() => { res.status(200).send('Pushed Successfully!'); }) .catch((error) => { console.log('Push failed:', error); res.sendStatus(500); }); }); app.listen(8080);
上述代码使用 web-push
包来初始化 Push 服务,并通过 Express 框架搭建了一个简单的 HTTP 服务器。在 app.post('/push')
回调函数中,我们接收 SSE 服务器推送过来的消息体,并根据需要生成推送通知消息的标题和内容。
最后,通过调用 webPush.sendNotification
方法向浏览器推送消息。
实现 Safari 推送的前端页面
最后是前端页面的实现。在这里,我们需要使用声明式订阅 SWE(Service Worker Extension)和注册 Service Worker。下面是一个基本的前端页面的实现示例:
<button id="subscribe-button" onclick="subscribe()">Subscribe</button> <script> let swRegistration = null; function subscribe() { // 注册 Service Worker navigator.serviceWorker.register('sw.js') .then((registration) => { swRegistration = registration; // 声明式订阅 SWE return swRegistration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array('publicKey') }); }) .then((subscription) => { // 向后端推送 subscription sendToServer(subscription); }); } function urlBase64ToUint8Array(base64String) { // base64 转 uint8array 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; } function sendToServer(subscription) { // 向后端发送 subscription const options = { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ subscription: subscription, message: 'Hello, world!' }) }; fetch('http://localhost:8080/push', options) .then((response) => { console.log('Push Success:', response); }) .catch((error) => { console.log('Push Failed:', error); }); } </script>
上述代码中,我们首先在页面上添加了一个“Subscribe”按钮,然后通过调用 navigator.serviceWorker.register
方法注册了 Service Worker。接下来,我们使用 swRegistration.pushManager.subscribe
方法在客户端完成声明式订阅 SWE,然后将生成的 subscription
通过 sendToServer
方法发送到后端。
总结
本文介绍了如何结合 Server-Sent Events 和 Safari 推送,实现了基于 SSE 的 Safari 推送服务。首先,我们实现了 SSE 服务器,通过 res.write
方法将 SSE 数据发送到浏览器。然后,通过 EventSource
对象在客户端上监听 SSE 服务器发送过来的消息。接下来,通过苹果提供的 Web Push Notification API,我们在后端实现了 Safari 推送服务。最后,在前端实现了注册 Service Worker 和声明式订阅 SWE,最终我们实现了完整的 SSE + Safari 推送的示例。
参考文献
- Server-Sent Events 教程
- Safari 推送通知介绍
- Web Push Notification API 文档
- Web 开发中的推送通知
- web-push npm 包介绍
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65ba4f2dadd4f0e0ff2d7234