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.

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. Der nächste Schritt besteht darin, unseren Service Worker zu "registrieren".

Wenn wir einen Service Worker registrieren, teilen wir dem Browser mit, wo sich die Service Worker-Datei befindet. Bei der Datei handelt es sich zwar immer noch um JavaScript, aber der Browser "gewährt ihr Zugriff" auf die Service 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. Bei Fehlern jeglicher Art 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 Berechtigungsaufforderung interagiert hat, indem er auf „Zulassen“, „Blockieren“ oder einfach auf „Schließen“ geklickt hat, wird das Ergebnis als String ausgegeben: 'granted', 'default' oder 'denied'.

Im Beispielcode oben wird das von askPermission() zurückgegebene Promise aufgelöst, wenn die Berechtigung gewährt wird. Andernfalls wird ein Fehler ausgegeben, bei dem das Promise abgelehnt wird.

Ein Grenzfall, den Sie behandeln müssen, ist, wenn die Nutzenden auf die Schaltfläche „Blockieren“ klicken. 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.

Wir sehen uns später 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, ist das eine symbolische Vereinbarung mit dem Browser, 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 die Push API derzeit nur für Abos, die für den Nutzer sichtbare Nachrichten verursachen. 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 eine pauschale stille Push-Funktion in Chrome nie implementiert werden. Stattdessen erforschen Spezifikationsautoren das Konzept einer Budget-API, mit der Webanwendungen eine bestimmte Anzahl von stillen Push-Nachrichten auf Basis der Nutzung einer Webanwendung zulassen können.

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. Die Webanwendung wird in einem Browser geladen, Sie rufen subscribe() auf und übergeben Ihren öffentlichen Anwendungsserverschlüssel.
  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 jemand Nachrichten an die Nutzer einer Anwendung sendet.

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

Technisch gesehen ist die 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 Spezifikation, die definiert, was der Anwendungsserverschlüssel sein sollte, ist die VAPID-Spezifikation. Wenn Sie etwas lesen, das sich auf "Anwendungsserverschlüssel" oder "VAPID-Schlüssel" bezieht, denken Sie daran, dass es sich dabei um denselben Wert 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 UI mit diesem Ablauf funktioniert. Wenn Sie aber mehr Kontrolle haben möchten (und ich denke, dies werden die meisten Entwickler), bleiben Sie bei der Notification.requestPermission() API, 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 einen 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 dasselbe Ergebnis manuell so zusammenfassen:

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 unserem Nutzer 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. Ein Muster, um dies zu verhindern, besteht darin, den Nutzer nach jeder empfangenen Benachrichtigung erneut 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 als Web-Push-Protokoll bezeichnet 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, auf dem er Nachrichten empfangen möchte, für Push registrieren. Dabei muss der Nutzer auf jedem Gerät eine Berechtigung erteilen.

Weitere Informationen

Codelabs