web-dev-qa-db-ja.com

Googleへのデータ/ペイロードの送信Chrome Javascriptによるプッシュ通知

私は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を取得しようとしましたが、これは未定義です。

誰にもアイデアや提案はありますか?

22
Adriano Tadao

そのデータを取得するには、「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も使用します。同じことをお勧めします。

0
user5803705

残念ながら 意図した動作 のようです:

ChromeのPush APIの現在の実装のマイナス面は、Pushメッセージでペイロードを送信できないことです。いいえ、何もありません。これは、将来の実装では、ペイロードは、プッシュメッセージングエンドポイントに送信される前にサーバーで暗号化する必要がありますこの方法では、プッシュプロバイダーがどのようなものであっても、エンドポイントはプッシュペイロードのコンテンツを簡単に表示できません。 HTTPS証明書の検証が不十分で、サーバーとプッシュプロバイダー間の中間者攻撃が発生しますが、この暗号化はまだサポートされていないため、その間に必要な情報を取得するためにフェッチリクエストを実行する必要があります通知を入力します。

上記のように、回避策は、プッシュを受信した後にバックエンドに連絡して、サードパーティのサーバーに保存されたデータを取得することです。

30
gauchofunky

@gauchofunkyの答えは正しいです。 Chromium dev slackチャンネルと@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);
  });
});

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. In Chrome 45 the 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;
});

現在、プッシュ通知は何か役に立つものを得るためにかなりの作業を必要とします-私は本当にペイロードを楽しみにしています!

17
Andy Gaskell

実際、ペイロードはChrome 50(リリース日-2016年4月19日)に実装する必要があります。InChrome 50(およびデスクトップ上のFirefoxの現在のバージョン)クライアントが追加のリクエストを行わないように、Pushとともに任意のデータを送信できます。すべてのペイロードデータは暗号化する必要があります。

開発者からの暗号化の詳細は次のとおりです。 https://developers.google.com/web/updates/2016/03/web-Push-encryption?hl=en

14

私はこの問題に出くわしました。 firefoxおよびchrome(バージョン50+)の新しいバージョンは、ペイロード転送をサポートしています。

dev docs here これがどのように機能するかの実装の詳細。注意すべき重要なことは、Google GCMまたはクライアント/ホーム(おそらくどちらか)が暗号化されていない場合、実際にペイロードを完全に無視することです。

This Webサイトには、プッシュを実行する方法とサービスワーカーを介して取得する方法の両方のクライアント/サーバー実装があります。例が使用するプッシュライブラリは、単に 通常のREST呼び出し)のラッパー

service worker実装例:

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

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

Server実装例:

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
Jay

これを実現するには、次の手順に従います。

ブラウザ内:

subscriptionオブジェクトを取得して保存する必要があるため、サーバーからアクセスできます。 詳細について

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

サーバー内:

install web-push npm package

そして、次のようなWebプッシュを送信します。

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