美文网首页
WebPush实现网络推送

WebPush实现网络推送

作者: 指尖轻敲 | 来源:发表于2023-11-30 15:36 被阅读0次

    概述

    Web Push是在PWA开发中配合ServiceWorker实现消息推送的技术。大致流程分为以下几步:

    1. 浏览器向推送服务器发起推送订阅请求
    2. 浏览器拿到订阅完成后推送服务器返回的订阅信息,发送到应用服务器进行保存。
    3. 后面应用服务器通过web push向推送服务发送消息,同时携带之前保存的订阅信息,用于让推送服务器知道要推送给谁。
    4. 推送服务器接收到消息后,根据订阅信息将消息推送给指定的浏览器,serviceWorker需要提前监听push事件。

    接下来看一下每一步的具体操作。

    客户端发起订阅

    首先前端在serviceWorker注册完成之后可以获取到一个serviceWorkerRegistration实例对象。使用这个对象的pushManager.subscribe方法进行订阅。

    const registrtion = await navigator.serviceWorker.register('./sw.js')
    const pushSubscription = await registrtion.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: base64ToUint8Array('BAvgL3epn4A5JN5tBhKsjpOUXnqYcbOGEitOw8KOAedS69NgcZ814NuP7IV5Ei5Wz2VBralB1WYkazdLBRMq3yw')
    })
    
    // 当然也可以取消订阅,先不关心
    pushSubscription.unsubscribe().then(function () {
      console.log('取消订阅成功!')
    })
    

    userVisibleOnly参数为了保证推送对用户可见,设置为true即可;applicationServerKey服务器密钥的生成需要借助一个npm包web-push来生成,可以通过代码生成:

    const webpush = require('web-push')
    const vapidKeys = webpush.generateVAPIDKeys()
    

    也可以全局安装web-push,通过命令生成:

    web-push generate-vapid-keys --json
    

    以上可以生成一个这样的一对公钥和私钥(不加--json也可以,生成的格式不用而已):

    {
        "publicKey":"BAvgL3epn4A5JN5tBhKsjpOUXnqYcbOGEitOw8KOAedS69NgcZ814NuP7IV5Ei5Wz2VBralB1WYkazdLBRMq3yw",
        "privateKey":"SMIFmRPZHtLhIfxEqkbzanZg7NH1FlrFF8cf_K3BeWQ"
    }
    

    以上在应用服务器生成,公钥给到客户端(就是订阅是需要传递的),同时因为公钥是base64编码的字符串,需要将其转成Uint8Array格式才能作subscribe为参数传入。私钥是需要保存在应用服务器的。

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

    推送服务器返回的订阅信息pushSubscription,是需要传递给应用服务器的,格式如下:

    {
        "endpoint": "https://fcm.googleapis.com/xxxxx",
        "keys": {
            "p256dh": "xxxxxx",
            "auth": "xxxxxx"
        }
    }
    

    应用服务器保存订阅信息

    拿到订阅信息之后需要保存到我们自己的应用服务器,可以通过pushSubscription.getKey('p256dh')pushSubscription.getKey('auth')获取密钥和校验码信息,由于通过getKey获取到的是ArrayBuffer类型,所以需要转成base64字符串便于传输。应用服务器可以提供一个接口,比如/api/saveSubscription,然后把数据存储到数据库中。

    fetch('/api/saveSubscription', {
        method: 'post',
        body: JSON.stringify({
            endpoint: pushSubscription.endpoint,
            keys: {
                p256dh: uint8ArrayToBase64(pushSubscription.getKey('p256dh')),
                auth: uint8ArrayToBase64(pushSubscription.getKey('auth')),
            }
        })
    })
    function uint8ArrayToBase64 (arr) {
      return btoa(String.fromCharCode.apply(null, new Uint8Array(arr)))
    }
    

    应用服务器推送消息

    我们的Node应用服务端需要使用web-push库实现向推送服务器发送消息。首先通过setVapidDetails进行配置。然后需要配置在云服务申请到的GCM API Key,最后通过sendNotification发送,第一个参数是之前保存的pushSubscription对象,第二参数是要发送给浏览器前端的数据信息。

    const webpush = require('web-push')
    const vaipdKeys = {
        publicKey: '', // 公钥
        privateKey: '' //私钥
    }
    webpush.setVapidDetails(
        'mailto:邮箱地址.com', // 注意前缀mailto:不能丢
        vaipdKeys.publicKey,
        vaipdKeys.privateKey
    )
    webpush.setGCMAPIKey('<Your GCM API Key Here>')
    webpush.sendNotification(pushSubscription, JSON.stringfy({}))
    

    浏览器监听推送

    在serviceWorker文件sw.js中,在注册完成之后可以进行监听。这样就可以收到推送消息了。

    self.addEventListener('push', e => {
        if(e.data) {
            // 推送的消息在data属性中,通过text()可以解析成字符串
            console.log(e.data.text())
            // 也可以通过json()解析成json等
        }
    })
    

    注意:如果不翻一下,推送服务器在国内是用不了的哦。

    补充

    Notification

    配合使用的 Notification API,判断是否支持并允许通知:

    function requestNotificationPermission () {
        if (!window.Notification) {
            return Promise.reject('浏览器不支持桌面通知')
        }
        return Notification.requestPermission().then(function (permission) {
            if (permission === 'granted') {
                return Promise.resolve()
            }
            return Promise.reject('用户已禁止通知权限')
        })
    }
    
    整体流程图
    webpush流程图.png

    相关文章

      网友评论

          本文标题:WebPush实现网络推送

          本文链接:https://www.haomeiwen.com/subject/royqgdtx.html