使用Javascript将数据/有效负载发送到Google Chrome推送通知

时间:2015-05-19 20:42:42

标签: javascript google-chrome push-notification

我正在处理Google Chrome推送通知,我尝试将有效负载发送给Google Chrome工作人员,但我不知道如何收到此有效负载。

我有一个API来创建和保存我的数据库中的通知,我需要通过https://android.googleapis.com/gcm/send发送值并在worker.js上接收

这是我的worker.js

    self.addEventListener('push', function(event) {
      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
        })
      );
    });

这就是我调用GCM的方式

curl --header "Authorization: key=AIzaSyDQjYDxeS9MM0LcJm3oR6B7MU7Ad2x2Vqc" --header  "Content-Type: application/json" https://android.googleapis.com/gcm/send -d "{ \"data\":{\"foo\":\"bar\"}, \"registration_ids\":[\"APA91bGqJpCmyCnSHLjY6STaBQEumz3eFY9r-2CHTtbsUMzBttq0crU3nEXzzU9TxNpsYeFmjA27urSaszKtA0WWC3yez1hhneLjbwJqlRdc_Yj1EiqLHluVwHB6V4FNdXdKb_gc_-7rbkYkypI3MtHpEaJbWsj6M5Pgs4nKqQ2R-WNho82mnRU\"]}"

我试图获得event.data但是,这是未定义的。

有没有人有任何想法或消化?

6 个答案:

答案 0 :(得分:30)

不幸的是,这似乎是intended behavior

  

当前在Chrome中实施Push API的缺点是   您无法使用推送消息发送有效负载。不,没什么。该   这样做的原因是,在未来的实施中,有效载荷将具有   在发送到推送消息之前在服务器上加密   端点。这样终端,无论是推送提供者,都会   无法轻松查看推送有效负载的内容。这也是   防止其他漏洞,如HTTPS验证不佳   服务器和服务器之间的证书和中间人攻击   推送提供商。但是,此加密尚不支持,所以在   与此同时,您需要执行获取请求以获取信息   需要填写通知。

如上所述,解决方法是在收到推送后联系您的后端并在第三方服务器上获取存储的数据。

答案 1 :(得分:17)

@ gauchofunky的回答是正确的。在Chromium dev slack channel和@gauchofunky的人们的一些指导下,我能够拼凑出一些东西。以下是解决当前局限的方法;希望我的答案很快就会过时!

首先弄清楚你将如何在后端保留通知。我使用Node / Express和MongoDB使用Mongoose,我的架构如下所示:

var NotificationSchema = new Schema({
  _user: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
  subscriptionId: String,
  title: String,
  body: String,
  sent: { type: Boolean, default: false }
});

如果您想要更改图标,请务必添加图标。我每次都使用相同的图标,因此我的服务人员硬编码。

找出正确的REST Web服务需要一些思考。 GET似乎是一个简单的选择,但获得通知的调用会导致副作用,因此GET已经出局。我最终选择了POST/api/notifications,身体为{subscriptionId: <SUBSCRIPTION_ID>}。在该方法中,我们基本上执行出队:

var subscriptionId = req.body.subscriptionId;

Notification
.findOne({_user: req.user, subscriptionId: subscriptionId, sent: false})
.exec(function(err, notification) {
  if(err) { return handleError(res, err); }
  notification.sent = true;
  notification.save(function(err) {
    if(err) { return handleError(res, err); }
    return res.status(201).json(notification);
  });
});

在我们制作fetch之前,我们需要在服务工作者中确保获得订阅。

self.addEventListener('push', function(event) {
  event.waitUntil(
    self.registration.pushManager.getSubscription().then(function(subscription) {
      fetch('/api/notifications/', {
        method: 'post',
        headers: {
          'Authorization': 'Bearer ' + self.token,
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(subscription)
      })
      .then(function(response) { return response.json(); })
      .then(function(data) {
        self.registration.showNotification(data.title, {
          body: data.body,
          icon: 'favicon-196x196.png'
        });
      })
      .catch(function(err) {
        console.log('err');
        console.log(err);
      });
    })
  );
});

还值得注意的是,订阅对象已从Chrome 43更改为Chrome 45.在Chrome 45中,subscriptionId属性已被删除,只需注意一些内容 - 此代码已编写为可以使用Chrome 43。

我想对我的后端进行经过身份验证的调用,因此我需要弄清楚如何从我的Angular应用程序中获取JWT给我的服务工作者。我最终使用postMessage。这是我在注册服务人员后所做的事情:

navigator.serviceWorker.register('/service-worker.js', {scope:'./'}).then(function(reg) {
  var messenger = reg.installing || navigator.serviceWorker.controller;
  messenger.postMessage({token: $localStorage.token});
}).catch(function(err) {
  console.log('err');
  console.log(err);
});

在服务工作者中收听消息:

self.onmessage.addEventListener('message', function(event) {
  self.token = event.data.token;
});

奇怪的是,该听众可以在Chrome 43中使用,但不适用于Chrome 45. Chrome 45可以使用这样的处理程序:

self.addEventListener('message', function(event) {
  self.token = event.data.token;
});

现在推送通知需要花费大量工作才能获得有用的东西 - 我真的很期待有效载荷!

答案 2 :(得分:14)

实际上,有效负载应在Chrome 50中实施(发布日期 - 2016年4月19日)。在Chrome 50(以及桌面上当前版本的Firefox)中,您可以随推送一些任意数据,以便客户端可以避免发出额外请求。所有有效载荷数据必须加密。

以下是开发人员的加密详细信息:https://developers.google.com/web/updates/2016/03/web-push-encryption?hl=en

答案 3 :(得分:4)

我刚遇到这个问题。较新版本的firefox和chrome(版本50+)支持有效负载转移。

开发人员here详细说明了如何运作的实施。需要注意的一点是,谷歌GCM或可能的客户端/丁目(我不知道哪一个)如果没有加密就会完全忽略有效载荷。

This网站有客户端/服务器实现如何通过服务工作者进行推送和检索。示例使用的推送库仅为wrapper around a normal REST call

服务工作者 示例实现:

self.addEventListener('push', function(event) {
var payload = event.data ? event.data.text() : 'no payload';

event.waitUntil(
   self.registration.showNotification('ServiceWorker Cookbook', {
     body: payload,
   })
 );
});

服务器 示例实现:

var webPush = require('web-push');

webPush.setGCMAPIKey(process.env.GCM_API_KEY);

module.exports = function(app, route) {
 app.post(route + 'register', function(req, res) {
 res.sendStatus(201);
});

app.post(route + 'sendNotification', function(req, res) {
  setTimeout(function() {
   webPush.sendNotification(req.body.endpoint, {
     TTL: req.body.ttl,
     payload: req.body.payload,
     userPublicKey: req.body.key,
     userAuth: req.body.authSecret,
   }).then(function() {
    res.sendStatus(201);
   });
  }, req.body.delay * 1000);
 });
};

客户端javascript 打印出必填字段的实施示例。

navigator.serviceWorker.register('serviceWorker.js')
.then(function(registration) {

    return registration.pushManager.getSubscription()
        .then(function(subscription) {
            if (subscription) {
                return subscription;
            }
            return registration.pushManager.subscribe({
                userVisibleOnly: true
            });
        });
}).then(function(subscription) {
    var rawKey = subscription.getKey ? subscription.getKey('p256dh') : '';
    key = rawKey ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey))) : '';
    var rawAuthSecret = subscription.getKey ? subscription.getKey('auth') : '';
    authSecret = rawAuthSecret ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawAuthSecret))) : '';
    endpoint = subscription.endpoint;
    console.log("Endpoint: " + endpoint);
    console.log("Key: " + key);
    console.log("AuthSecret: " + authSecret);
});

答案 4 :(得分:0)

要检索该数据,您需要解析&#34; event.data.text()&#34;到JSON对象。我猜你的东西已经更新,因为你试图让它工作,但它现在有效。红颜薄命!

然而,由于我自己在搜索解决方案时已经发布了这篇文章,其他人可能会喜欢一个有效的答案。这是:

// Push message event handler
self.addEventListener('push', function(event) {

  // If true, the event holds data
  if(event.data){

    // Need to parse to JSON format
    // - Consider event.data.text() the "stringify()"
    //   version of the data
    var payload = JSON.parse(event.data.text());
    // For those of you who love logging
    console.log(payload); 

    var title = payload.data.title;
    var body  = payload.data.body;
    var icon  = './assets/icons/icon.ico'
    var tag   = 'notification-tag';

    // Wait until payload is fetched
    event.waitUntil(
      self.registration.showNotification(title, {
        body: body,
        icon: icon,
        tag: tag,
        data: {} // Keeping this here in case I need it later
      })
    );

  } else {
    console.log("Event does not have data...");
  }

}); // End push listener

// Notification Click event
self.addEventListener('notificationclick', function(event) {
  console.log("Notification Clicked");
}); // End click listener

就个人而言,我将创建一个&#34;泛型&#34;如果我的数据很时髦,也会使用try / catch。我建议做同样的事情。

答案 5 :(得分:0)

请按照以下步骤操作:

在浏览器中:

您需要获取subscription对象并保存它,以便您的服务器可以访问它:Read more about it

navigator.serviceWorker.ready.then(serviceWorkerRegistration => {
            serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly: true})
              .then(subscription => {
                  //save subscription.toJSON() object to your server
    })});

在服务器中:

安装web-push npm package

并发送这样的网络推送:

    const webpush = require('web-push');


    setImmediate(async () => {

      const params = {
        payload: {title: 'Hey', body: 'Hello World'}
      };
      //this is the subscription object you should get in the browser. This is a demo of how it should look like
      const subscription = {"endpoint":"https://android.googleapis.com/gcm/send/deC24xZL8z4:APA91bE9ZWs2KvLdo71NGYvBHGX6ZO4FFIQCppMsZhiTXtM1S2SlAqoOPNxzLlPye4ieL2ulzzSvPue-dGFBszDcFbSkfb_VhleiJgXRA8UwgLn5Z20_77WroZ1LofWQ22g6bpIGmg2JwYAqjeca_gzrZi3XUpcWHfw","expirationTime":null,"keys":{"p256dh":"BG55fZ3zZq7Cd20vVouPXeVic9-3pa7RhcR5g3kRb13MyJyghTY86IO_IToVKdBmk_2kA9znmbqvd0-o8U1FfA3M","auth":"1gNTE1wddcuF3FUPryGTZOA"}};

      if (subscription.keys) {
        params.userPublicKey = subscription.keys.p256dh;
        params.userAuth      = subscription.keys.auth;
      }

// this key you should take from firebase console for example
// settings -> cloud messaging -> Server key     
webpush.setGCMAPIKey('AAAASwYmslc:APfA91bGy3tdKvuq90eOvz4AoUm6uPtbqZktZ9dAnElrlH4gglUiuvereTJJWxz8_dANEQciX9legijnJrxvlapI84bno4icD2D0cdVX3_XBOuW3aWrpoqsoxLDTdth86CjkDD4JhqRzxV7RrDXQZd_sZAOpC6f32nbA');

      try {
        const r = await webpush.sendNotification(subscription, JSON.stringify(params));
        console.log(r);
      }
      catch (e) {
        console.error(e);
      }
    });