web-dev-qa-db-fra.com

Envoi de données/charge utile à la notification push de Google Chrome avec Javascript

Je travaille sur la notification Google Chrome Push et j’essaie d’envoyer la charge utile à l’agent de Google Chrome, mais je ne sais pas du tout comment je la reçois.

J'ai une API pour créer et enregistrer les notifications dans ma base de données et j'ai besoin d'envoyer les valeurs via le https://Android.googleapis.com/gcm/send et de les recevoir sur le worker.js

C'est mon 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
        })
      );
    });

Et voici comment j'appelle le 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\"]}"

J'ai essayé d'obtenir event.data mais, ceci n'est pas défini.

Quelqu'un a-t-il une idée ou une suggestion?

23
Adriano Tadao

Pour récupérer ces données, vous devez analyser "event.data.text ()" dans un objet JSON. Je suppose que quelque chose a été mis à jour depuis que vous avez essayé de faire en sorte que cela fonctionne, mais cela fonctionne maintenant. Malchanceux! 

Cependant, puisque je suis parvenu à ce poste en cherchant moi-même une solution, d’autres voudraient probablement une réponse satisfaisante. C'est ici:

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

Personnellement, je créerai une notification "générique" au cas où mes données seraient géniales, et utiliserais aussi try/catch. Je suggère de faire la même chose.

0
FoxDonut

Malheureusement, cela ressemble à un comportement souhaité :

L’avantage de l’implémentation actuelle de l’API Push dans Chrome est que vous ne pouvez pas envoyer de charge utile avec un message Push. Non, rien. Le La raison en est que dans une implémentation future, la charge utile aura être crypté sur votre serveur avant qu’il ne soit envoyé à une messagerie Push point final. De cette façon, le point de terminaison, quel que soit le fournisseur Push, sera ne pas être en mesure de voir facilement le contenu de la charge Push. Ça aussi protège contre d'autres vulnérabilités telles qu'une mauvaise validation de HTTPS certificats et attaques d'interception entre votre serveur et le Fournisseur Push. Cependant, ce cryptage n’est pas encore supporté, donc dans le fichier En attendant, vous devrez effectuer une requête de récupération pour obtenir des informations nécessaire pour renseigner une notification.

Comme indiqué ci-dessus, la solution de contournement consiste à contacter votre serveur après la réception du Push et à récupérer les données stockées sur le serveur tiers.

29
gauchofunky

La réponse de @ gauchofunky est correcte. Grâce aux conseils des utilisateurs du canal Chromium dev slack et de @gauchofunky, j'ai pu rassembler quelque chose. Voici comment contourner les limitations actuelles. si tout va bien ma réponse deviendra bientôt obsolète!

Commencez par déterminer comment vous allez conserver les notifications sur votre serveur. J'utilise Node/Express et MongoDB avec Mongoose et mon schéma ressemble à ceci:

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

Assurez-vous d'ajouter une icône si vous souhaitez modifier l'icône. J'utilise la même icône à chaque fois pour que le mien soit codé en dur dans le technicien. 

Déterminer le bon service Web REST a réfléchi. GET semblait être un choix facile, mais l'appel à la notification entraînant des effets indésirables, GET est désactivé. J'ai fini par aller avec un POST à /api/notifications avec un corps de {subscriptionId: <SUBSCRIPTION_ID>}. Dans le cadre de la méthode, nous effectuons essentiellement une suppression de file d'attente:

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

Dans le technicien, nous devons absolument obtenir l'abonnement avant de créer la variable 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);
      });
    })
  );
});

Il convient également de noter que l'objet d'abonnement est passé de Chrome 43 à Chrome 45. Dans Chrome 45, la propriété subscriptionId a été supprimée. Il s'agit simplement d'un élément à surveiller: ce code a été écrit pour fonctionner avec Chrome 43.

Je voulais faire des appels authentifiés à mon backend, donc je devais trouver comment obtenir le JWT de mon application Angular à mon technicien. J'ai fini par utiliser postMessage. Voici ce que je fais après avoir inscrit le technicien:

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

Dans le technicien, écoutez le message:

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

Curieusement, cet auditeur fonctionne avec Chrome 43 mais pas avec Chrome 45. Chrome 45 fonctionne avec un gestionnaire comme celui-ci: 

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

Pour le moment, les notifications push requièrent un peu de travail pour que quelque chose d’utile se passe - je suis vraiment impatient de recevoir des données utiles!

17
Andy Gaskell

En fait, la charge utile devrait être implémentée dans Chrome 50 (date de publication: le 19 avril 2016). Dans Chrome 50 (et dans la version actuelle de Firefox sur le bureau), vous pouvez envoyer des données arbitraires avec le Push pour que le client puisse éviter de faire la demande supplémentaire. Toutes les données utiles doivent être cryptées. 

Voici les informations de chiffrement du développeur: https://developers.google.com/web/updates/2016/03/web-Push-encryption?hl=fr

14
Viacheslav Gruzdov

Je viens de rencontrer ce problème. Les nouvelles versions de firefox et de chrome (version 50+) prennent en charge le transfert de charge utile. 

La dev docs ici détaille l’implémentation de son fonctionnement. Une chose importante à noter est que Google GCM ou éventuellement client/chome (je ne sais pas lequel) ignorera réellement la charge si elle n’est pas chiffrée. 

Ce site Web a les deux implémentations client/serveur expliquant comment effectuer le Push et la récupération par le biais de techniciens de maintenance. La bibliothèque Push utilisée par les exemples est simplement un wrapper autour d’un appel normal REST

service worker exemple d'implémentation:

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

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

Exemple de Serveur:

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 côté client exemple d'implémentation de l'impression des champs obligatoires.

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

Suivez ces étapes pour y parvenir:

Dans le navigateur:

Vous devez obtenir l’objet subscription et le sauvegarder afin que votre serveur y ait accès: En savoir plus

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

Sur le serveur:

installer le paquet web-Push npm

Et envoyez un push web comme ceci:

    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