资讯专栏INFORMATION COLUMN

web-push实现原理及细节介绍

hufeng / 2363人阅读

摘要:一使用动机与原理简述相较于移动端本地应用,站点常常缺少一项常用的功能推送通知。发送数据时,数据必须编码出于安全性考虑。二实现细节按照上一部分所说,首先进行用户订阅。

一、web push 使用动机与原理简述

相较于移动端本地应用,web站点常常缺少一项常用的功能:推送通知。此处的推送通知一般指由浏览器实现的消息推送,换个说法,就是用户在打开浏览器时,不需要进入特定的网站,就能收到该网站推送而来的消息,例如:新评论,新动态等等。

那么web push究竟是怎样的一个流程呢,简单地说,可以分为三个步骤:

客户端完成请求订阅一个用户的逻辑

服务端调用遵从web push协议的接口,传送消息推送(push message)到推送服务器(该服务器由浏览器决定,开发者所能做的只有控制发送的数据)

推送服务器将该消息推送至对应的浏览器,用户收到该推送

第一步,客户端请求订阅用户,过程如下:

说明一下这三步,在第一步之前,应用服务器需要生成应用服务器密钥(application server keys),其作用是标识该服务器,保证每次发消息推送的都是同一个服务器。然后,客户端将会请求用户授权消息推送,一旦用户授权,浏览器就会生成一个PushScription,然后这个PushScription将会被发送至服务器,存入数据库,在后面的消息推送中使用。

第二步,应用服务器发送web push协议标准的api,触发推送服务器的消息推送,其中headers必须配置正确,且传送的数据必须是比特流。

应用服务器发送消息推送请求(目的是为了将更新推送到用户的浏览器),为了向推送服务器发出请求,需要查看先前获得的PushScription,取出其中的endpoint,即为推送服务器配置给该用户的访问点。

一个PushScription对象如下:

{
  "endpoint": "https://random-push-service.com/some-kind-of-unique-id-1234/v2/",
  "keys": {
    "p256dh" :
"BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA_0QTpQtUbVlUls0VJXg7A8u-Ts1XbjhazAkj7I99e8QcYP7DkM=",
    "auth"   : "tBHItJI5svbpez7KI4CCXg=="
  }
}

其中的endpoint包含了推送服务器域名,path后面的部分为推送服务器为每个用户分配的一个标识符。

发送数据时,数据必须编码(出于安全性考虑)。推送服务器在接收到这样一个请求之后,立即开始监听用户浏览器是否处于在线状态,若是,则将消息推送发送至浏览器。

第三步,浏览器端接收消息推送,触发push事件并展示

浏览器在接收到推送服务器发来的推送后,将其解码并触发一个push事件。Service Worker由于它可以在浏览器页面未打开,浏览器未打开时执行,因此一般选择它完成web push的最后一步,即响应push事件完成展示通知等业务逻辑。

二、web push实现细节

按照上一部分所说,首先进行用户订阅。

首先注册一个Service Worker,若注册成功,返回的Promise为resolve状态,如下:

function registerServiceWorker() {
  return navigator.serviceWorker.register("service-worker.js")
  .then(function(registration) {
    console.log("Service worker successfully registered.");
    return registration;
  })
  .catch(function(err) {
    console.error("Unable to register service worker.", err);
  });
}

随后测试window环境下是否有Notification对象(此处以chrome为例,若使用firefox,uc等浏览器,需要遵循其相应标准,调用对应对象方法或引入JS SDK包),测试成功,调用Notification.requestPermission请求用户授权发送推送,若授权成功,将会返回"granted"。

接下来要做的就是使用注册好的Service Worker对象,调用pushManager.subscribe方法,从客户端获得刚刚所说的PushScription对象。

function subscribeUserToPush() {
  return navigator.serviceWorker.register("service-worker.js")
  .then(function(registration) {
    const subscribeOptions = {
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(
        "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U"
      )
    };

    return registration.pushManager.subscribe(subscribeOptions);
  })
  .then(function(pushSubscription) {
    console.log("Received PushSubscription: ", JSON.stringify(pushSubscription));
    return pushSubscription;
  });
}

userVisibleOnly是为了保证推送对用户可见,application server key则如前文所说,是推送服务器用以识别应用服务器的密钥,这里的密钥包含了公钥和私钥,传输的是公钥。同时,PushScription的endpoint也是在这个过程中生成的,生成公钥和私钥可以使用web-push库。

这里再次说明一下推送服务器的不可选择性,在调用subscribe生成PushScription时,浏览器会向它指定的中转服务器发送请求来生成endpoint和其余部分,这是没法控制的。

PushScription中的auth和p256dh是用来控制带载荷的push message的。

获取到PushScription对象后,将其发往应用服务器,此处简化了存储,使用nedb存下PushScription并返回Promise:

function saveSubscriptionToDatabase(subscription) {
  return new Promise(function(resolve, reject) {
    db.insert(subscription, function(err, newDoc) {
      if (err) {
        reject(err);
        return;
      }

      resolve(newDoc._id);
    });
  });
};

存储完毕后,接下来就是开发后台管理逻辑,使得管理员能够触发向用户推送消息的事件,应用服务器所做的逻辑就是遍历在数据库中存储的所有PushScription并推送消息,以下是使用web-push库完成配置密钥及联系邮箱的示例:

const vapidKeys = {
  publicKey:
"BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U",
  privateKey: "UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls"
};

webpush.setVapidDetails(
  "mailto:web-push-book@gauntface.com",
  vapidKeys.publicKey,
  vapidKeys.privateKey
);

不要忘了配置你在谷歌云服务(例如FCM)申请到的GCMApiKey:

webpush.setGCMAPIKey("");

配置完成后,就可以将subscription发送出去,使用web-push的sendNotification接口:

webpush.sendNotification(pushSubscription, "Your Push Payload Text");

推送服务器发送消息后,会触发浏览器的push事件,为了控制service worker的逻辑,需要使用event.waitUntil方法,此方法接收一个promise参数,在promise变为resolved状态后,浏览器就会检查通知是否已被展示,若是,则关闭service worker。

如果不处理未正常执行的promise,部分浏览器如chrome会展示默认消息框:

展示一个通知调用的为showNotification方法,传的参数包括title等,如下:

var title = "Yay a message.";
var body = "We have received a push message.";
var icon = "/images/icon-192x192.png";
var tag = "simple-push-demo-notification-tag";

event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
);

而展示notification时,除了控制它的视图层以外,也可以控制它的逻辑层,例如点击消息通知后进行某些操作等等,在先前调用showNotification时可以传入一些参数,例如,根据不同的action执行不同的操作:

self.addEventListener("notificationclick", function(event) {
  if (!event.action) {
    // Was a normal notification click
    console.log("Notification Click.");
    return;
  }

  switch (event.action) {
    case "coffee-action":
      console.log("User ❤️️"s coffee.");
      break;
    case "doughnut-action":
      console.log("User ❤️️"s doughnuts.");
      break;
    case "gramophone-action":
      console.log("User ❤️️"s music.");
      break;
    case "atom-action":
      console.log("User ❤️️"s science.");
      break;
    default:
      console.log(`Unknown action clicked: "${event.action}"`);
      break;
  }
});
三、兼容性及其他问题 与ajax轮询、http长连接、WebSocket的对比

ajax轮询是通过客户端不断向服务端发送http请求,若有新消息就取回的模式保持数据实时更新,但这种方式需要服务器有很快的处理速度和资源

http长连接是客户端向服务器发送请求后,若服务器没有新数据要发送,就不返回response,一旦有了新数据返回了response,客户端就立刻再发一个request,周而复始。事实上这是把http协议的不对称性从客户端转移到了服务端

WebSocket是HTML5中提出的一个新标准(也可视之为协议),客户端在发送请求时在请求头加入额外的字段,以标识这是一个基于WebSocket协议的连接,服务器根据这个请求头生成响应,与客户端建立起WebSocket连接,之后服务端有新消息时,直接向客户端推送即可

不同浏览器兼容性

chrome采用的推送服务器为gcm或fcm,firefox也有自己的推送服务器

uc前些时间构建了自己的推送服务器,引入其官网上的sdk包,申请使用后即可用于开发

大家要是感兴趣可以看看我的github~https://github.com/proempire,这个项目可能会继续跟进

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107097.html

相关文章

  • 【PWA学习与实践】(5)在Web中进行服务端消息推送

    摘要:本文是学习与实践系列的第五篇文章。实际上,消息推送与提醒是两个功能和。在这一篇里,我们先来学习如何使用进行消息推送。而当服务端要推送消息时,会使用私钥对发送的数据进行数字签名,并根据数字签名生成一个叫请求头。 《PWA学习与实践》系列文章已整理至gitbook - PWA学习手册,文字内容已同步至learning-pwa-ebook。转载请注明作者与出处。 本文是《PWA学习与实践》系...

    suemi 评论0 收藏0
  • Android 性能监控系列一(原理篇)

    摘要:全称应用性能管理监控后面我会通过一系列的文章来介绍的原理框架设计与实现等等。在应用构建期间,通过修改字节码的方式来进行字节码插桩就是实现自动化的方案之一。 showImg(https://segmentfault.com/img/bVbbRX6?w=1995&h=1273); 欢迎关注微信公众号:BaronTalk,获取更多精彩好文! 一. 前言 性能问题是导致 App 用户流失的罪魁...

    yacheng 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<