【问题标题】:Sending data / payload to the Google Chrome Push Notification with Javascript使用 Javascript 将数据/有效负载发送到 Google Chrome 推送通知
【发布时间】:2015-07-31 21:05:28
【问题描述】:

我正在处理 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,但是,这是未定义的。

有人有什么想法或建议吗?

【问题讨论】:

    标签: javascript google-chrome push-notification


    【解决方案1】:

    不幸的是,它看起来像intended behavior

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

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

    【讨论】:

    • 感谢您的宝贵时间。我以前读过它,但我不想相信=l。是个坏消息...我将在服务器上发出新请求以获取有关我的通知的所有信息。再次感谢您
    • 确实如此。希望他们能尽快支持加密的有效载荷,因为没有有效载荷,这个功能几乎没用。
    • @AndyGaskell 如果我理解得很好,这个想法是你总是对最后一个事件感兴趣,否则你的服务器需要为你的设备保留某种包含所有排队警报的收件箱。无论哪种方式,您都需要在您身边实现这一点。到目前为止,推送仅表明“某事”发生了。
    • @gauchofunky 谢谢。在周末工作之后,您基本上需要一个队列服务器端,其中包含您的用户 ID(如果适用)、订阅 ID 和通知信息(标题、消息、图标)。在服务人员的fetch 中获取用户上下文和subscriberId 有点痛苦,但我认为我现在走在了正确的轨道上。再次感谢。
    • @eloibm 没错。这是 Chromium 项目的问题 - code.google.com/p/chromium/issues/detail?id=486040
    【解决方案2】:

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

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

    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);
      });
    });
    

    在 Service Worker 中,我们需要确保在创建 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 一起使用而编写的。

    我想对我的后端进行经过身份验证的调用,因此我需要弄清楚如何将 JWT 从我的 Angular 应用程序获取到我的服务工作者。我最终使用了postMessage。以下是我注册 service worker 后的操作:

    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);
    });
    

    在 service worker 中监听消息:

    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;
    });
    

    现在推送通知需要做很多工作才能让一些有用的东西运行起来——我真的很期待有效载荷!

    【讨论】:

    • 谢谢安迪这个信息真的很有帮助
    【解决方案3】:

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

    这里是来自开发者的加密细节:https://developers.google.com/web/updates/2016/03/web-push-encryption?hl=en

    【讨论】:

    • 拜托,扩展你的答案,链接不够。
    • 请看下面我的回答
    【解决方案4】:

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

    开发文档here 详细介绍了其工作原理的实现。需要注意的重要一点是,如果未加密,google GCM 或可能的 client/chome(我不知道是哪一个)实际上会完全忽略有效负载。

    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);
    });
    

    【讨论】:

    • 是的 - 应该。 (虽然我已经有一段时间没有尝试过了)。当我第一次写这篇文章时它起作用了:)
    • 实际上我正在尝试在 chrome 中推送通知,但它不起作用。相同的代码在 Firefox 中运行良好。所以只是想知道你能帮忙解决我的问题吗?我在这里有我的代码:stackoverflow.com/questions/40042783/… 我已将控制台放在 webpush.sendNotification() 中保证控制台 vlaue 永远不会出现,如果它在 chrome 中运行。但在 Firefox 中我可以看到控制台值。
    • 升级你的 chrome。它必须是 50+ 版本。
    • Chrome 版本 53.0.2785.101(64 位)
    【解决方案5】:

    要检索该数据,您需要将“event.data.text()”解析为 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
    

    就我个人而言,我将创建一个“通用”通知,以防我的数据很时髦,并且还将使用 try/catch。我建议也这样做。

    【讨论】:

    • 这很好。我不再使用这个了,但是,它对另一个人有好处!谢谢老兄。 =]
    【解决方案6】:

    按照以下步骤实现:

    在浏览器中:

    您需要获取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);
          }
        });
    

    【讨论】:

      猜你喜欢
      • 2021-04-26
      • 1970-01-01
      • 1970-01-01
      • 2018-04-04
      • 2017-05-31
      • 1970-01-01
      • 2016-07-19
      • 2016-12-08
      • 1970-01-01
      相关资源
      最近更新 更多