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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30335749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:00:57  来源:igfitidea点击:

Sending data / payload to the Google Chrome Push Notification with Javascript

javascriptgoogle-chromepush-notification

提问by Adriano Tadao

I'm working on the Google Chrome Push Notification and I'm trying to send the payload to the google chrome worker but, I have no idea how I receive this payload.

我正在处理 Google Chrome 推送通知,我正在尝试将有效负载发送给 google chrome 工作器,但是,我不知道我是如何接收此有效负载的。

I have an API to create and save the notifications in my database and I need send the values through the https://android.googleapis.com/gcm/sendand receive on the worker.js

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

This is my 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
        })
      );
    });

And this is how I'm calling the GCM

这就是我调用 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\"]}"

I tried to get event.databut, this is undefined.

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

Does anyone have any idea or sugestion?

有没有人有任何想法或建议?

采纳答案by Jay

To retrieve that data, you need to parse "event.data.text()" to a JSON object. I'm guessing something was updated since you tried to get this to work, but it works now. Unlucky!

要检索该数据,您需要将“event.data.text()”解析为 JSON 对象。我猜自从你试图让它工作以来,有些东西已经更新了,但现在它可以工作了。倒霉!

However, since I made it to this post when searching for a solution myself, others would probably like a working answer. Here it is:

但是,由于我在自己寻找解决方案时进入了这篇文章,因此其他人可能会想要一个有效的答案。这里是:

// 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

Personally, I will be creating a "generic" notification in case my data is funky, and will also be using try/catch. I suggest doing the same.

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

回答by gauchofunky

Unfortunately it seems like an intended behavior:

不幸的是,这似乎是一种预期的行为

A downside to the current implementation of the Push API in Chrome is that you can't send a payload with a push message. Nope, nothing. The reason for this is that in a future implementation, payload will have to be encrypted on your server before it's sent to a push messaging endpoint. This way the endpoint, whatever push provider it is, will not be able to easily view the content of the push payload. This also protects against other vulnerabilities like poor validation of HTTPS certificates and man-in-the-middle attacks between your server and the push provider. However, this encryption isn't supported yet, so in the meantime you'll need to perform a fetch request to get information needed to populate a notification.

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

As stated above, the workaround is to contact back your backend after receiving the push and fetch the stored data on the 3rd party server.

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

回答by Andy Gaskell

@gauchofunky's answer is correct. With some guidance from the folks on the Chromium dev slack channel and @gauchofunky I was able to piece something together. Here's how to work around the current limitations; hopefully my answer becomes obsolete soon!

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

First figure out how you're going to persist notifications on your backend. I'm using Node/Express and MongoDB with Mongoose and my schema looks like this:

首先弄清楚您将如何在后端保持通知。我在 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 }
});

Be sure to add an icon if you'd like to alter the icon. I use the same icon every time so mine's hardcoded in the service worker.

如果您想更改图标,请务必添加一个图标。我每次都使用相同的图标,所以我的在 service worker 中是硬编码的。

Figuring out the correct REST web service took some thought. GET seemed like an easy choice but the call to get a notification causes side effects, so GET is out. I ended up going with a POSTto /api/notificationswith a body of {subscriptionId: <SUBSCRIPTION_ID>}. Within the method we basically perform a dequeue:

找出正确的 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);
  });
});

In the service worker we need to for sure get the subscription before we make the fetch.

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

It's also worth noting that the subscription object changed from Chrome 43 to Chrome 45. In Chrome 45 the subscriptionIdproperty was removed, just something to look out for - this code was written to work with Chrome 43.

还值得注意的是,订阅对象从 Chrome 43 更改为 Chrome 45。在 Chrome 45 中,该subscriptionId属性已被删除,只是需要注意 - 此代码是为与 Chrome 43 一起使用而编写的。

I wanted to make authenticated calls to my backend so I needed to figure out how to get the JWT from my Angular application to my service worker. I ended up using postMessage. Here's what I do after registering the service worker:

我想对我的后端进行经过身份验证的调用,所以我需要弄清楚如何将 JWT 从我的 Angular 应用程序获取到我的服务工作者。我最终使用了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);
});

In the service worker listen for the message:

在 service worker 中监听消息:

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

Strangely enough, that listener works in Chrome 43 but not Chrome 45. Chrome 45 works with a handler like this:

奇怪的是,该侦听器在 Chrome 43 中有效,但在 Chrome 45 中无效。 Chrome 45 使用如下处理程序:

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

Right now push notifications take quite a bit of work to get something useful going - I'm really looking forward to payloads!

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

回答by Viacheslav Gruzdov

Actually, payload should be implemented in Chrome 50 (release date - April 19, 2016). In Chrome 50 (and in the current version of Firefox on desktop) you can send some arbitrary data along with the push so that the client can avoid making the extra request. All payload data must be encrypted.

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

Here is the the encryption details from developer : https://developers.google.com/web/updates/2016/03/web-push-encryption?hl=en

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

回答by Jay

I just ran into this problem. Newer versions of firefox and chrome( version 50+) support payload transferring.

The dev docs heredetails the implementation on how this works. An important thing to note is that google GCM or possibly client/chome (I dont know which one) will actually ignore the payload entirely if it is not encrypted.

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

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

Thiswebsite has both client/server implementations of how to do the push and retrieval through service workers. The push library that examples use is merely a wrapper around a normal REST call

这个网站有客户端/服务器实现如何通过 Service Worker 进行推送和检索。示例使用的推送库只是普通 REST 调用包装器

service workerexample implementation:

服务工作者示例实现:

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

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

Serverexample implementation:

服务器示例实现:

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

Client side javascriptimplementation example of printing out the the required fields.

打印出所需字段的客户端 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);
});

回答by YardenST

Follow these steps to achieve this:

请按照以下步骤实现此目的:

In the browser:

在浏览器中:

You need to get the subscriptionobject and save it, so your server has access to it: Read more about it

您需要获取subscription对象并保存它,以便您的服务器可以访问它:阅读有关它的更多信息

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

In the server:

在服务器中:

install web-push npm package

安装web-push npm 包

And send a web push like this:

并发送这样的网络推送:

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