zoukankan      html  css  js  c++  java
  • 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推送消息了 。

  • 相关阅读:
    python基础之元组、文件操作、编码、函数、变量
    python---基础之模块,列表,元组,字典
    python成长之路-----day1-----作业(登录程序和三级菜单)
    k8s的port、targetport、nodeport之间的区别
    查找并删除文件
    systemctl自定义service
    中标麒麟7.0源
    springboot问题,没有主清单属性
    iso搭建本地源
    添加路由
  • 原文地址:https://www.cnblogs.com/hellohello/p/8441188.html
Copyright © 2011-2022 走看看