service worker 消息推送
Posted HelloHello233
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了service worker 消息推送相关的知识,希望对你有一定的参考价值。
https://developers.google.com/web/fundamentals/codelabs/push-notifications/?hl=en
首先下载源码:
git clone https://github.com/GoogleChrome/push-notifications.git
设置如下选项方便开发:
开始
注册之后记录sw实例:
navigator.serviceWorker.register(\'sw.js\') .then(function(swReg) { console.log(\'Service Worker is registered\', swReg); swRegistration = swReg; })
生成key:
https://web-push-codelab.glitch.me/。生成了一个相互对应的public key 与 private key
然后把public key记录到 applicationServerPublicKey变量上。
判断当前sw是否已经订阅过消息推送了:
swRegistration.pushManager.getSubscription() .then(function(subscription) { isSubscribed = !(subscription === null); if (isSubscribed) { console.log(\'User IS subscribed.\'); } else { console.log(\'User is NOT subscribed.\'); } });
使用之前生成的public key来订阅消息推送:
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey); // subscribe 会给推送服务器发送一个网络请求 swRegistration.pushManager.subscribe({ userVisibleOnly: true, // 用于显示请求权限的界面,所以这个值基本必须为true,否则获取不到权限的话,当前promise会被reject applicationServerKey: applicationServerKey }).then(function (subscription) { // 订阅成功。subscription 就是推送服务器返回的信息 console.log(\'User is subscribed.\'); updateSubscriptionOnServer(subscription); // 在这个自定义函数中,我们应该把订阅信息发送给后端 isSubscribed = true; }).catch(function (err) { console.log(\'Failed to subscribe the user: \', err); }); // 工具函数| function urlB64ToUint8Array(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; }
执行订阅的时候,界面上会有如下弹框来请求消息推送的显示权限:
点击同意的话,则订阅成功。但如果用户点击了拒绝,则app没办法再次显示这个弹框而且没有消息推送,以下这个值会为true:
Notification.permission === \'denied\'
手动点击这里(ask),可以撤销权限,使弹窗再次弹出来,方便开发测试:
处理消息推送
我们需要在sw中监听push事件,来接收服务器发来的消息推送:
self.addEventListener(\'push\', function(event) { console.log(\'[Service Worker] Push Received.\'); console.log(`[Service Worker] Push had this data: "${event.data.text()}"`); const title = \'Push Codelab\'; const options = { body: \'Yay it works.\', icon: \'images/icon.png\', badge: \'images/badge.png\' //仅仅用在安卓 }; // showNotification 用于显示一个通知
// waitUntil :使sw等待直至这个promise被处理,否则有可能这个promise没被处理,sw 就被浏览器终止了 event.waitUntil(self.registration.showNotification(title, options)); });
测试:在这里点击push:
屏幕左下角就会看到这个通知:
但是点击这个通知是没什么响应的,需要我们去注册一个点击事件:
self.addEventListener(\'notificationclick\', function(event) { console.log(\'[Service Worker] Notification click Received.\'); event.notification.close(); // 关闭这个通知 event.waitUntil( clients.openWindow(\'https://developers.google.com/web/\') // 打开一个标签 ); });
发送消息推送
以上订阅成功后返回的subscription,将它 JSON.stringify(subscription) 后的字符串粘贴到 https://web-push-codelab.glitch.me/ 就可以发送用于测试的消息推送了(注意要用页面所在的key来订阅才可以)。
同理在实际应用中,我们后端也需要这个subscription信息来发送消息推送。步骤如下(使用 web-push):
创建firebase项目,里面的key为(用来作为GCM API key):
然后在https://web-push-codelab.glitch.me/ 中生成的public/private key为(其实也可以用webpush.generateVAPIDKeys来生成):
接着来订阅消息推送:
function urlB64ToUint8Array(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; } const applicationServerPublicKey = \'BP0bPsBFRO4JI4WPI-0Hztl49AX2mjfPxr5SAmiu9i1C4T1X2EFQvuoCekow-JD9Gs3aHlkxstVm9UTndHA0YM8\'; function subscribeUser() { const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey); swRegistration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: applicationServerKey }) .then(function(subscription) { console.log(\'User is subscribed.\'); updateSubscriptionOnServer(subscription); isSubscribed = true; updateBtn(); }) .catch(function(err) { console.log(\'Failed to subscribe the user: \', err); updateBtn(); }); }
node服务器来发送消息推送:
const webpush = require(\'web-push\'); // VAPID keys should only be generated only once. // const vapidKeys = webpush.generateVAPIDKeys(); webpush.setGCMAPIKey(\'AIzaSyAPNqXa931TMPdEx5im92uDQmWQKfKFJNo\'); webpush.setVapidDetails( \'mailto:947133297@qq.com\', "BP0bPsBFRO4JI4WPI-0Hztl49AX2mjfPxr5SAmiu9i1C4T1X2EFQvuoCekow-JD9Gs3aHlkxstVm9UTndHA0YM8", "Y23-foXK_oHtxOA5whmR61RBbyqqm9Sxnl-bapZPghQ" ); // This is the same output of calling JSON.stringify on a PushSubscription const pushSubscription = { endpoint: \'https://fcm.googleapis.com/fcm/send/fN0CygRBHVo:APA91bH4FB9bkE6RjD6v758TaNoHIx4IhUxdSm_bcFMPRRnyY4IcTlID9md6AwAdhUhqE7HzbL76WY6Wzak7MGmtrJ5InYAwYP31B-mc-TXRCnKQwUKxjIPe1Kv6-U_S672rG_8jVmpJ\', keys: { auth: \'uOxqcnlXYQIyDucqXeWeeA==\', p256dh: \'BFoO1hMB5kpWA4lPx2fKZGiyw3Qd-3n9afeE3jrJ62Bna66LsHQmCSIjo0Q9t2UF6MZdzyqe6cNkNbSGpNpmX6I=\' } }; webpush.sendNotification(pushSubscription, \'Your Push Payload Text\').then(()=>{ console.log("发送完成") }).catch((err)=>{ console.log("被拒绝") console.log(err) })
因为在中国被墙的原因,以上代码运行会报错:
被拒绝 { Error: connect ETIMEDOUT 172.217.160.106:443 at Object._errnoException (util.js:1024:11) at _exceptionWithHostPort (util.js:1046:20) at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1182:14) code: \'ETIMEDOUT\', errno: \'ETIMEDOUT\', syscall: \'connect\', address: \'172.217.160.106\', port: 443 }
查看issue之后,发现有人针对这个问题提交了一个PR,但是没有被应用,即master分支上还是存在这个问题。
取消订阅
swRegistration.pushManager.getSubscription() .then(function(subscription) { if (subscription) { // TODO: Tell application server to delete subscription return subscription.unsubscribe(); } }) .catch(function(error) { console.log(\'Error unsubscribing\', error); })
并且要记得通知后端,不要往这个subscription推送消息了 。
以上是关于service worker 消息推送的主要内容,如果未能解决你的问题,请参考以下文章
service-worker 可以停止 GCM api 发送的推送通知吗?