Nachrichten mit Web-Push-Bibliotheken senden

Matt Gaunt

Eines der Probleme bei der Arbeit mit Web-Push-Mitteilungen ist, dass das Auslösen einer Push-Mitteilung extrem umständlich ist. Um eine Push-Nachricht auszulösen, muss eine Anwendung eine POST-Anfrage an einen Push-Dienst senden, die dem Web-Push-Protokoll entspricht. Wenn Sie Push-Mitteilungen in allen Browsern verwenden möchten, müssen Sie VAPID (auch als Anwendungsserverschlüssel bezeichnet) verwenden. Dazu müssen Sie einen Header mit einem Wert festlegen, der nachweist, dass Ihre Anwendung Nutzer benachrichtigen kann. Wenn Sie Daten mit einer Push-Nachricht senden möchten, müssen die Daten verschlüsselt werden und bestimmte Header müssen hinzugefügt werden, damit der Browser die Nachricht richtig entschlüsseln kann.

Das Hauptproblem beim Auslösen von Push-Benachrichtigungen besteht darin, dass es bei Problemen schwierig ist, die Ursache zu ermitteln. Mit der Zeit und einer breiteren Browserunterstützung wird es besser, aber es ist noch lange nicht einfach. Aus diesem Grund empfehlen wir dringend, eine Bibliothek für die Verschlüsselung, Formatierung und Auslösung Ihrer Push-Nachricht zu verwenden.

Im nächsten Abschnitt erfahren Sie, was die Bibliotheken tun. Im Moment konzentrieren wir uns auf die Verwaltung von Abos und die Verwendung einer vorhandenen Web-Push-Bibliothek, um Push-Anfragen zu senden.

In diesem Abschnitt verwenden wir die Node-Bibliothek „web-push“. Andere Sprachen unterscheiden sich zwar, aber nicht allzu sehr. Wir konzentrieren uns auf Node, da es sich um JavaScript handelt und es für Leser am zugänglichsten sein sollte.

Wir gehen die folgenden Schritte durch:

  1. Sende ein Abo an unser Backend und speichere es.
  2. Gespeicherte Abos abrufen und eine Push-Nachricht auslösen.

Abos speichern

Das Speichern und Abfragen von PushSubscription-Elementen aus einer Datenbank hängt von der serverseitigen Sprache und der Datenbankauswahl ab. Es kann jedoch hilfreich sein, ein Beispiel zu sehen, wie das geht.

Auf der Demo-Webseite wird die PushSubscription über eine einfache POST-Anfrage an unser Backend gesendet:

function sendSubscriptionToBackEnd(subscription) {
  return fetch('/api/save-subscription/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(subscription),
  })
    .then(function (response) {
      if (!response.ok) {
        throw new Error('Bad status code from server.');
      }

      return response.json();
    })
    .then(function (responseData) {
      if (!(responseData.data && responseData.data.success)) {
        throw new Error('Bad response from server.');
      }
    });
}

Der Express-Server in unserer Demo hat einen übereinstimmenden Anfrage-Listener für den /api/save-subscription/-Endpunkt:

app.post('/api/save-subscription/', function (req, res) {

In diesem Pfad prüfen wir das Abo, um sicherzustellen, dass die Anfrage in Ordnung ist und nicht mit Müll gefüllt ist:

const isValidSaveRequest = (req, res) => {
  // Check the request body has at least an endpoint.
  if (!req.body || !req.body.endpoint) {
    // Not a valid subscription.
    res.status(400);
    res.setHeader('Content-Type', 'application/json');
    res.send(
      JSON.stringify({
        error: {
          id: 'no-endpoint',
          message: 'Subscription must have an endpoint.',
        },
      }),
    );
    return false;
  }
  return true;
};

Wenn das Abo gültig ist, müssen wir es speichern und eine entsprechende JSON-Antwort zurückgeben:

return saveSubscriptionToDatabase(req.body)
  .then(function (subscriptionId) {
    res.setHeader('Content-Type', 'application/json');
    res.send(JSON.stringify({data: {success: true}}));
  })
  .catch(function (err) {
    res.status(500);
    res.setHeader('Content-Type', 'application/json');
    res.send(
      JSON.stringify({
        error: {
          id: 'unable-to-save-subscription',
          message:
            'The subscription was received but we were unable to save it to our database.',
        },
      }),
    );
  });

In dieser Demo werden die Abos mit nedb gespeichert. Das ist eine einfache dateibasierte Datenbank. Sie können aber jede beliebige Datenbank verwenden. Wir verwenden diese Methode nur, da sie keine Einrichtung erfordert. Für die Produktion sollten Sie etwas Zuverlässigeres verwenden. (Ich bleibe lieber beim guten alten MySQL.)

function saveSubscriptionToDatabase(subscription) {
  return new Promise(function (resolve, reject) {
    db.insert(subscription, function (err, newDoc) {
      if (err) {
        reject(err);
        return;
      }

      resolve(newDoc._id);
    });
  });
}

Push-Nachrichten senden

Wenn es um das Senden einer Push-Nachricht geht, benötigen wir letztendlich ein Ereignis, das den Prozess zum Senden einer Nachricht an Nutzer auslöst. Eine gängige Methode ist die Erstellung einer Administratorseite, auf der Sie die Push-Nachricht konfigurieren und auslösen können. Sie können jedoch ein Programm erstellen, das lokal ausgeführt wird, oder einen anderen Ansatz wählen, mit dem Sie auf die Liste der PushSubscriptions zugreifen und den Code ausführen können, um die Push-Nachricht auszulösen.

Unsere Demo hat eine Seite, die der Seite eines Administrators ähnelt, auf der Sie ein Push-Mitteilung auslösen können. Da es sich nur um eine Demo handelt, ist es eine öffentliche Seite.

Ich werde jeden Schritt durchgehen, der erforderlich ist, damit die Demo funktioniert. Es sind einfache Schritte, damit alle mitkommen können, auch diejenigen, die noch keine Erfahrung mit Node haben.

Beim Besprechen des Abonnierens eines Nutzers haben wir erwähnt, dass den Optionen subscribe() eine applicationServerKey hinzugefügt wird. Dieser private Schlüssel wird auf dem Backend benötigt.

In der Demo werden diese Werte unserer Node-Anwendung so hinzugefügt (langweiliger Code, ich weiß, aber ich möchte nur, dass Sie wissen, dass es keine Zauberei gibt):

const vapidKeys = {
  publicKey:
    'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
  privateKey: 'UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls',
};

Als Nächstes müssen wir das web-push-Modul für unseren Node-Server installieren:

npm install web-push --save

Dann fordern wir in unserem Node-Script das web-push-Modul so an:

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

Jetzt können wir das web-push-Modul verwenden. Zuerst müssen wir dem web-push-Modul unsere Anwendungsserverschlüssel mitteilen. Sie werden auch als VAPID-Schlüssel bezeichnet, da dies der Name der Spezifikation ist.

const vapidKeys = {
  publicKey:
    'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
  privateKey: 'UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls',
};

webpush.setVapidDetails(
  'mailto:web-push-book@gauntface.com',
  vapidKeys.publicKey,
  vapidKeys.privateKey,
);

Beachten Sie, dass wir auch den String „mailto:“ hinzugefügt haben. Dieser String muss entweder eine URL oder eine E-Mail-Adresse in Form eines „mailto“-Links sein. Diese Information wird im Rahmen der Anfrage zum Auslösen eines Pushes an den Web-Push-Dienst gesendet. So kann ein Web-Push-Dienst, falls er den Absender kontaktieren muss, einige Informationen dazu haben.

Das web-push-Modul ist jetzt einsatzbereit. Im nächsten Schritt wird eine Push-Nachricht ausgelöst.

In der Demo wird das fiktive Admin-Steuerfeld verwendet, um Push-Nachrichten auszulösen.

Screenshot der Seite „Verwaltung“

Wenn Sie auf die Schaltfläche „Push-Nachricht auslösen“ klicken, wird eine POST-Anfrage an /api/trigger-push-msg/ gesendet. Das ist das Signal für unser Backend, Push-Nachrichten zu senden. Daher erstellen wir die Route in Express für diesen Endpunkt:

app.post('/api/trigger-push-msg/', function (req, res) {

Wenn diese Anfrage eingeht, rufen wir die Abos aus der Datenbank ab und lösen für jedes eine Push-Nachricht aus.

return getSubscriptionsFromDatabase().then(function (subscriptions) {
  let promiseChain = Promise.resolve();

  for (let i = 0; i < subscriptions.length; i++) {
    const subscription = subscriptions[i];
    promiseChain = promiseChain.then(() => {
      return triggerPushMsg(subscription, dataToSend);
    });
  }

  return promiseChain;
});

Die Funktion triggerPushMsg() kann dann die Web-Push-Bibliothek verwenden, um eine Nachricht an das angegebene Abo zu senden.

const triggerPushMsg = function (subscription, dataToSend) {
  return webpush.sendNotification(subscription, dataToSend).catch((err) => {
    if (err.statusCode === 404 || err.statusCode === 410) {
      console.log('Subscription has expired or is no longer valid: ', err);
      return deleteSubscriptionFromDatabase(subscription._id);
    } else {
      throw err;
    }
  });
};

Der Aufruf von webpush.sendNotification() gibt ein Versprechen zurück. Wenn die Nachricht erfolgreich gesendet wurde, wird das Versprechen erfüllt und wir müssen nichts unternehmen. Wenn das Versprechen abgelehnt wird, müssen Sie den Fehler untersuchen, da Sie dadurch erfahren, ob die PushSubscription noch gültig ist.

Um die Art des Fehlers eines Push-Dienstes zu ermitteln, sollten Sie sich den Statuscode ansehen. Fehlermeldungen variieren je nach Push-Dienst und einige sind hilfreicher als andere.

In diesem Beispiel wird nach den Statuscodes 404 und 410 gesucht, den HTTP-Statuscodes für „Nicht gefunden“ und „Nicht mehr verfügbar“. Wenn wir eine dieser Meldungen erhalten, ist das Abo abgelaufen oder nicht mehr gültig. In diesen Fällen müssen wir die Abos aus unserer Datenbank entfernen.

Bei einem anderen Fehler throw err wir einfach, wodurch das von triggerPushMsg() zurückgegebene Versprechen abgelehnt wird.

Einige der anderen Statuscodes werden im nächsten Abschnitt behandelt, wenn wir uns das Web-Push-Protokoll genauer ansehen.

Nachdem wir die Abos durchgegangen sind, müssen wir eine JSON-Antwort zurückgeben.

.then(() => {
res.setHeader('Content-Type', 'application/json');
    res.send(JSON.stringify({ data: { success: true } }));
})
.catch(function(err) {
res.status(500);
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({
    error: {
    id: 'unable-to-send-messages',
    message: `We were unable to send messages to all subscriptions : ` +
        `'${err.message}'`
    }
}));
});

Wir haben die wichtigsten Schritte zur Implementierung bereits besprochen:

  1. Erstellen Sie eine API, um Abos von unserer Website an unser Back-End zu senden, damit sie in einer Datenbank gespeichert werden können.
  2. Erstellen Sie eine API, um das Senden von Push-Nachrichten auszulösen (in diesem Fall eine API, die über das gefälschte Admin-Dashboard aufgerufen wird).
  3. Alle Abos aus unserem Backend abrufen und mit einer der Web-Push-Bibliotheken eine Nachricht an jedes Abo senden.

Unabhängig von Ihrem Backend (Node, PHP, Python usw.) sind die Schritte zur Implementierung von Push identisch.

Was genau leisten diese Web-Push-Bibliotheken für uns?

Weitere Informationen

Code labs