Abonnieren eines Nutzers

Matt Gaunt

Der erste Schritt besteht darin, die Erlaubnis des Nutzers einzuholen, ihm Push-Nachrichten zu senden. Dann können wir eine PushSubscription abrufen.

Die JavaScript API ist recht einfach. Sehen wir uns den Logikablauf an.

Funktionserkennung

Zuerst müssen wir prüfen, ob der aktuelle Browser Push-Mitteilungen unterstützt. Wir können mit zwei einfachen Prüfungen feststellen, ob Push unterstützt wird.

  1. Prüfen Sie, ob serviceWorker in navigator vorhanden ist.
  2. Prüfen Sie, ob PushManager in window vorhanden ist.
if (!('serviceWorker' in navigator)) {
  // Service Worker isn't supported on this browser, disable or hide UI.
  return;
}

if (!('PushManager' in window)) {
  // Push isn't supported on this browser, disable or hide UI.
  return;
}

Die Browserunterstützung für Service Worker und Push-Messaging wächst zwar schnell, aber es ist immer eine gute Idee, die Funktion für beide zu erkennen und schrittweise zu verbessern.

Service Worker registrieren

Durch die Feature-Erkennung wissen wir, dass sowohl Service Worker als auch Push unterstützt werden. Im nächsten Schritt registrieren wir unseren Service Worker.

Wenn wir einen Dienst-Worker registrieren, teilen wir dem Browser mit, wo sich die Datei des Dienst-Workers befindet. Die Datei besteht zwar weiterhin nur aus JavaScript, aber der Browser gewährt ihr „Zugriff“ auf die Dienst-Worker-APIs, einschließlich Push. Genauer gesagt führt der Browser die Datei in einer Service Worker-Umgebung aus.

Rufen Sie navigator.serviceWorker.register() auf, um einen Dienst-Worker zu registrieren, und geben Sie dabei den Pfad zu unserer Datei an. Beispiel:

function registerServiceWorker() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      console.log('Service worker successfully registered.');
      return registration;
    })
    .catch(function (err) {
      console.error('Unable to register service worker.', err);
    });
}

Diese Funktion teilt dem Browser mit, dass wir eine Service Worker-Datei haben und wo sie sich befindet. In diesem Fall befindet sich die Service Worker-Datei unter /service-worker.js. Im Hintergrund führt der Browser nach dem Aufruf von register() die folgenden Schritte aus:

  1. Laden Sie die Service Worker-Datei herunter.

  2. Führen Sie das JavaScript aus.

  3. Wenn alles richtig funktioniert und keine Fehler auftreten, wird das von register() zurückgegebene Versprechen erfüllt. Wenn Fehler auftreten, wird das Versprechen abgelehnt.

Wenn register() abgelehnt wird, prüfen Sie Ihr JavaScript in den Chrome-Entwicklertools auf Tippfehler und Fehler.

Wenn register() aufgelöst wird, wird eine ServiceWorkerRegistration zurückgegeben. Wir verwenden diese Registrierung, um auf die PushManager API zuzugreifen.

Browserkompatibilität der PushManager API

Unterstützte Browser

  • Chrome: 42.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 16.

Quelle

Berechtigung anfordern

Wir haben unseren Dienst-Worker registriert und sind bereit, den Nutzer zu abonnieren. Der nächste Schritt besteht darin, die Erlaubnis des Nutzers einzuholen, ihm Push-Mitteilungen zu senden.

Die API zum Abrufen der Berechtigung ist relativ einfach. Der Nachteil ist, dass die API vor Kurzem von einem Rückruf zu einem Promise gewechselt ist. Das Problem dabei ist, dass wir nicht wissen können, welche Version der API vom aktuellen Browser implementiert wird. Sie müssen also beide implementieren und verarbeiten.

function askPermission() {
  return new Promise(function (resolve, reject) {
    const permissionResult = Notification.requestPermission(function (result) {
      resolve(result);
    });

    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  }).then(function (permissionResult) {
    if (permissionResult !== 'granted') {
      throw new Error("We weren't granted permission.");
    }
  });
}

Im obigen Code ist der Aufruf von Notification.requestPermission() das wichtige Code-Snippet. Bei dieser Methode wird dem Nutzer eine Aufforderung angezeigt:

Aufforderung zur Berechtigungserteilung in der Desktop- und Mobilversion von Chrome

Sobald der Nutzer mit der Berechtigungsanfrage interagiert hat, indem er „Zulassen“, „Blockieren“ oder einfach nur das Fenster geschlossen hat, erhalten wir das Ergebnis als String: 'granted', 'default' oder 'denied'.

Im Beispielcode oben wird das von askPermission() zurückgegebene Versprechen erfüllt, wenn die Berechtigung gewährt wird. Andernfalls wird ein Fehler geworfen, wodurch das Versprechen abgelehnt wird.

Ein Grenzfall, den Sie behandeln müssen, ist, wenn der Nutzer auf die Schaltfläche „Blockieren“ klickt. In diesem Fall kann die Web-App den Nutzer nicht noch einmal um Erlaubnis bitten. Sie müssen die Blockierung Ihrer App manuell aufheben, indem Sie den Berechtigungsstatus ändern, der sich in einem Einstellungsbereich befindet. Überlegen Sie sich gut, wie und wann Sie den Nutzer um Erlaubnis bitten, denn wenn er auf „Blockieren“ klickt, lässt sich diese Entscheidung nicht so einfach rückgängig machen.

Die gute Nachricht ist, dass die meisten Nutzer gerne ihre Einwilligung geben, solange sie wissen, warum die Berechtigung angefordert wird.

Später sehen wir uns an, wie einige beliebte Websites um Erlaubnis bitten.

Nutzer mit PushManager abonnieren

Sobald wir unseren Dienst-Worker registriert und die Berechtigung erhalten haben, können wir einen Nutzer abonnieren, indem wir registration.pushManager.subscribe() aufrufen.

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

Beim Aufrufen der subscribe()-Methode übergeben wir ein options-Objekt, das sowohl erforderliche als auch optionale Parameter enthält.

Sehen wir uns alle Optionen an, die wir übergeben können.

Optionen vom Typ „userVisibleOnly“

Als Push-Benachrichtigungen in Browsern eingeführt wurden, war unklar, ob Entwickler eine Push-Nachricht senden und keine Benachrichtigung anzeigen sollten. Dies wird häufig als „Silent Push“ bezeichnet, da der Nutzer nicht weiß, dass im Hintergrund etwas passiert ist.

Es bestand die Befürchtung, dass Entwickler schädliche Dinge tun könnten, z. B. den Standort eines Nutzers kontinuierlich erfassen, ohne dass der Nutzer davon weiß.

Um dieses Szenario zu vermeiden und den Autoren von Spezifikationen Zeit zu geben, darüber nachzudenken, wie diese Funktion am besten unterstützt werden kann, wurde die Option userVisibleOnly hinzugefügt. Wenn Sie den Wert true übergeben, wird dem Browser symbolisch mitgeteilt, dass die Webanwendung jedes Mal eine Benachrichtigung anzeigt, wenn eine Push-Mitteilung empfangen wird (d. h. kein stiller Push).

Derzeit müssen Sie den Wert true übergeben. Wenn Sie den Schlüssel userVisibleOnly nicht angeben oder false übergeben, wird die folgende Fehlermeldung angezeigt:

Chrome unterstützt derzeit nur die Push API für Abos, die zu für Nutzer sichtbaren Mitteilungen führen. Sie können dies angeben, indem Sie stattdessen pushManager.subscribe({userVisibleOnly: true}) aufrufen. Weitere Informationen finden Sie unter https://goo.gl/yqv4Q4.

Derzeit sieht es so aus, als würde die allgemeine stille Push-Funktion in Chrome nie implementiert werden. Stattdessen prüfen die Verfasser der Spezifikation den Ansatz einer Budget-API, die Web-Apps eine bestimmte Anzahl von stillen Push-Nachrichten basierend auf der Nutzung einer Web-App ermöglicht.

Option „applicationServerKey“

Im vorherigen Abschnitt haben wir kurz „Anwendungsserverschlüssel“ erwähnt. „Anwendungsserverschlüssel“ werden von einem Push-Dienst verwendet, um die Anwendung zu identifizieren, die einen Nutzer abonniert, und dafür zu sorgen, dass dieselbe Anwendung dem Nutzer Nachrichten sendet.

Anwendungsserverschlüssel sind ein öffentliches und ein privates Schlüsselpaar, das für Ihre Anwendung einzigartig ist. Der private Schlüssel sollte für Ihre Anwendung geheim gehalten werden. Der öffentliche Schlüssel kann frei weitergegeben werden.

Die Option applicationServerKey, die an den subscribe()-Aufruf übergeben wird, ist der öffentliche Schlüssel der Anwendung. Der Browser gibt diese Informationen beim Abonnieren des Nutzers an einen Push-Dienst weiter. Der Push-Dienst kann dann den öffentlichen Schlüssel Ihrer Anwendung mit der PushSubscription des Nutzers verknüpfen.

Das folgende Diagramm veranschaulicht diese Schritte.

  1. Ihre Webanwendung wird in einem Browser geladen und Sie rufen subscribe() auf, wobei Sie Ihren öffentlichen Anwendungsserverschlüssel übergeben.
  2. Der Browser sendet dann eine Netzwerkanfrage an einen Push-Dienst, der einen Endpunkt generiert, diesen Endpunkt mit dem öffentlichen Schlüssel der Anwendung verknüpft und an den Browser zurückgibt.
  3. Der Browser fügt diesen Endpunkt dem PushSubscription hinzu, der über das subscribe()-Versprechen zurückgegeben wird.

Abbildung des öffentlichen Schlüssels des Anwendungsservers, der in der subscribe-Methode verwendet wird.

Wenn Sie später eine Push-Nachricht senden möchten, müssen Sie einen Autorisierungsheader erstellen, der Informationen enthält, die mit dem privaten Schlüssel Ihres Anwendungsservers signiert sind. Wenn der Push-Dienst eine Anfrage zum Senden einer Push-Nachricht erhält, kann er diesen signierten Autorisierungs-Header prüfen, indem er den öffentlichen Schlüssel abruft, der mit dem Endpunkt verknüpft ist, an den die Anfrage gesendet wird. Wenn die Signatur gültig ist, weiß der Push-Dienst, dass sie vom Anwendungsserver mit dem übereinstimmenden privaten Schlüssel stammen muss. Es ist im Grunde eine Sicherheitsmaßnahme, die verhindert, dass andere Personen Nachrichten an die Nutzer einer Anwendung senden.

Verwendung des privaten Schlüssels des Anwendungsservers beim Senden einer Nachricht

Technisch gesehen ist applicationServerKey optional. Für die einfachste Implementierung in Chrome ist es jedoch erforderlich und möglicherweise auch in anderen Browsern in Zukunft. In Firefox ist sie optional.

Die VAPID-Spezifikation definiert, wie der Anwendungsserverschlüssel sein sollte. Wenn du etwas über Anwendungsserverschlüssel oder VAPID-Schlüssel liest, denke daran, dass es sich dabei um dasselbe handelt.

Anwendungsserverschlüssel erstellen

Sie können öffentliche und private Anwendungsserverschlüssel unter web-push-codelab.glitch.me erstellen oder mit der Befehlszeile „web-push“ Schlüssel generieren. Gehen Sie dazu so vor:

    $ npm install -g web-push
    $ web-push generate-vapid-keys

Sie müssen diese Schlüssel nur einmal für Ihre Anwendung erstellen. Achten Sie darauf, den privaten Schlüssel vertraulich zu behandeln. (Ja, das habe ich gerade gesagt.)

Berechtigungen und subscribe()

Das Aufrufen von subscribe() hat eine Nebenwirkung. Wenn Ihre Webanwendung zum Zeitpunkt des Aufrufs von subscribe() keine Berechtigungen zum Anzeigen von Benachrichtigungen hat, fordert der Browser die Berechtigungen für Sie an. Das ist nützlich, wenn Ihre Benutzeroberfläche mit diesem Ablauf funktioniert. Wenn Sie jedoch mehr Kontrolle haben möchten (und ich denke, die meisten Entwickler werden das tun), sollten Sie bei der Notification.requestPermission() API bleiben, die wir zuvor verwendet haben.

Was ist ein PushSubscription?

Wir rufen subscribe() auf, übergeben einige Optionen und erhalten im Gegenzug ein Versprechen, das zu einem PushSubscription führt. Das ergibt folgenden Code:

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

Das PushSubscription-Objekt enthält alle erforderlichen Informationen, um eine Push-Nachricht an diesen Nutzer zu senden. Wenn Sie den Inhalt mit JSON.stringify() ausdrucken, sehen Sie Folgendes:

    {
      "endpoint": "https://some.pushservice.com/something-unique",
      "keys": {
        "p256dh":
    "BIPUL12DLfytvTajnryr2PRdAgXS3HGKiLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WAkAPIxr4gK0_dQds4yiI=",
        "auth":"FPssNDTKnInHVndSTdbKFw=="
      }
    }

endpoint ist die URL des Push-Dienstes. Wenn Sie eine Push-Nachricht auslösen möchten, senden Sie eine POST-Anfrage an diese URL.

Das keys-Objekt enthält die Werte, mit denen Nachrichtendaten verschlüsselt werden, die mit einer Push-Nachricht gesendet werden (wird später in diesem Abschnitt erläutert).

Regelmäßige Verlängerung, um Ablauf zu verhindern

Wenn Sie Push-Benachrichtigungen abonnieren, erhalten Sie oft eine PushSubscription.expirationTime von null. Theoretisch bedeutet das, dass das Abo nie abläuft (im Gegensatz zu einer DOMHighResTimeStamp, die den genauen Zeitpunkt angibt, zu dem das Abo abläuft). In der Praxis lassen Browser Abos jedoch häufig ablaufen, z. B. wenn längere Zeit keine Push-Benachrichtigungen empfangen wurden oder der Browser erkennt, dass der Nutzer keine App verwendet, die die Berechtigung für Push-Benachrichtigungen hat. Eine Möglichkeit, dies zu verhindern, besteht darin, den Nutzer bei jeder empfangenen Benachrichtigung neu zu abonnieren, wie im folgenden Snippet gezeigt. Dazu müssen Sie Benachrichtigungen häufig genug senden, damit das Abo nicht automatisch abläuft. Sie sollten die Vorteile und Nachteile legitimer Benachrichtigungsanforderungen sorgfältig gegen den unfreiwilligen Spam des Nutzers abwägen, nur damit das Abo nicht abläuft. Letztendlich sollten Sie nicht versuchen, den Browser daran zu hindern, Nutzer vor längst vergessenen Benachrichtigungsabonnements zu schützen.

/* In the Service Worker. */

self.addEventListener('push', function(event) {
  console.log('Received a push message', event);

  // Display notification or handle data
  // Example: show a notification
  const title = 'New Notification';
  const body = 'You have new updates!';
  const icon = '/images/icon.png';
  const tag = 'simple-push-demo-notification-tag';

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
  );

  // Attempt to resubscribe after receiving a notification
  event.waitUntil(resubscribeToPush());
});

function resubscribeToPush() {
  return self.registration.pushManager.getSubscription()
    .then(function(subscription) {
      if (subscription) {
        return subscription.unsubscribe();
      }
    })
    .then(function() {
      return self.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
      });
    })
    .then(function(subscription) {
      console.log('Resubscribed to push notifications:', subscription);
      // Optionally, send new subscription details to your server
    })
    .catch(function(error) {
      console.error('Failed to resubscribe:', error);
    });
}

Abo an deinen Server senden

Sobald Sie ein Push-Abo haben, sollten Sie es an Ihren Server senden. Wie du das machst, bleibt dir überlassen. Als kleiner Tipp: Mit JSON.stringify() kannst du alle erforderlichen Daten aus dem Aboobjekt abrufen. Alternativ können Sie das gleiche Ergebnis auch manuell zusammenstellen:

const subscriptionObject = {
  endpoint: pushSubscription.endpoint,
  keys: {
    p256dh: pushSubscription.getKeys('p256dh'),
    auth: pushSubscription.getKeys('auth'),
  },
};

// The above is the same output as:

const subscriptionObjectToo = JSON.stringify(pushSubscription);

So sendest du das Abo auf der Webseite:

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 Knotenserver empfängt diese Anfrage und speichert die Daten zur späteren Verwendung in einer Datenbank.

app.post('/api/save-subscription/', function (req, res) {
  if (!isValidSaveRequest(req, res)) {
    return;
  }

  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.',
          },
        }),
      );
    });
});

Mit den PushSubscription-Details auf unserem Server können wir unseren Nutzern jederzeit eine Nachricht senden.

Regelmäßige Verlängerung, um Ablauf zu verhindern

Wenn Sie Push-Benachrichtigungen abonnieren, erhalten Sie oft eine PushSubscription.expirationTime von null. Theoretisch bedeutet das, dass das Abo nie abläuft (im Gegensatz zu einer DOMHighResTimeStamp, die den genauen Zeitpunkt angibt, zu dem das Abo abläuft). In der Praxis lassen Browser Abos jedoch häufig ablaufen, z. B. wenn lange Zeit keine Push-Benachrichtigungen empfangen wurden oder der Browser erkennt, dass der Nutzer die App, für die die Berechtigung für Push-Benachrichtigungen gilt, nicht verwendet. Eine Möglichkeit, dies zu verhindern, besteht darin, den Nutzer bei jeder empfangenen Benachrichtigung neu zu abonnieren, wie im folgenden Snippet gezeigt. Dazu müssen Sie Benachrichtigungen häufig genug senden, damit das Abo nicht automatisch abläuft. Sie sollten die Vorteile und Nachteile legitimer Benachrichtigungsanforderungen sorgfältig gegen das Spammen des Nutzers abwägen, nur damit das Abo nicht abläuft. Letztendlich sollten Sie nicht versuchen, den Browser daran zu hindern, Nutzer vor längst vergessenen Benachrichtigungsabonnements zu schützen.

/* In the Service Worker. */

self.addEventListener('push', function(event) {
  console.log('Received a push message', event);

  // Display notification or handle data
  // Example: show a notification
  const title = 'New Notification';
  const body = 'You have new updates!';
  const icon = '/images/icon.png';
  const tag = 'simple-push-demo-notification-tag';

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
  );

  // Attempt to resubscribe after receiving a notification
  event.waitUntil(resubscribeToPush());
});

function resubscribeToPush() {
  return self.registration.pushManager.getSubscription()
    .then(function(subscription) {
      if (subscription) {
        return subscription.unsubscribe();
      }
    })
    .then(function() {
      return self.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
      });
    })
    .then(function(subscription) {
      console.log('Resubscribed to push notifications:', subscription);
      // Optionally, send new subscription details to your server
    })
    .catch(function(error) {
      console.error('Failed to resubscribe:', error);
    });
}

Häufig gestellte Fragen

Hier einige häufig gestellte Fragen:

Kann ich den Push-Dienst eines Browsers ändern?

Nein. Der Push-Dienst wird vom Browser ausgewählt. Wie wir beim subscribe()-Aufruf gesehen haben, sendet der Browser Netzwerkanfragen an den Push-Dienst, um die Details abzurufen, aus denen die PushSubscription besteht.

Jeder Browser verwendet einen anderen Push-Dienst. Haben sie nicht unterschiedliche APIs?

Für alle Push-Dienste wird dieselbe API erwartet.

Diese gängige API wird Web Push Protocol genannt und beschreibt die Netzwerkanfrage, die Ihre Anwendung senden muss, um eine Push-Nachricht auszulösen.

Wenn ich einen Nutzer auf seinem Computer abonniere, wird er dann auch auf seinem Smartphone abonniert?

Leider nicht. Ein Nutzer muss sich in jedem Browser für Push-Benachrichtigungen registrieren, in dem er Nachrichten erhalten möchte. Außerdem muss der Nutzer die Berechtigung auf jedem Gerät gewähren.

Weitere Informationen

Code labs