Web-Push-Benachrichtigungen sind kurze Nachrichten, die Websites an deine Geräte senden können, selbst wenn du die Website gerade nicht aktiv besuchst. Sie ähneln den Benachrichtigungen von Apps und werden vergleichbar auf deinem Desktop oder Mobilgerät angezeigt.
Hier findest du eine Anleitung, wie du Web-Push-Benachrichtigungen in deine Website einbinden und versenden kannst.
Service Worker vorausgesetzt
Damit Nachrichten zugestellt werden können, auch wenn man sich gar nicht auf der entsprechenden Website befindet, muss ein sogenannter Service Worker im Hintergrund laufen. Die Anleitung zur Einrichtung eines Service Workers solltest du also zuerst umsetzen, falls dir dieser noch fehlt. Damit sind dann alle Voraussetzungen erfüllt und wir können loslegen.
Wir sind nett und fragen zuerst, ob man wirklich Benachrichtigungen erhalten will. Dazu zeigen wir ein einladendes Icon oder einen Button an. Bei Klick öffnet sich eine Dialogbox mit der Anfrage.
Bei Klick auf den Ja-Button haben wir die Zustimmung eingeholt.
VAPID-Schlüssel erstellen
Zur Registrierung und fürs spätere Senden werden sogenannte VAPID-Schlüssel benötigt. Davon gibt es einen öffentlichen und einen privaten Schlüssel. Du kannst einen der vielen freien Generatoren nutzen, z.B. https://www.attheminute.com/vapid-key-generator.
Jeder der beiden Schlüssel ist in einer eigenen Datei abzulegen. Der öffentliche auf dem Web-Server. Dieser ist 88 Zeichen lang und könnte so aussehen:
Der private Schlüssel ist 44 Zeichen lang und ist auf dem Push-Server zu hinterlegen. Als Push-Server kann auch der eigene PC dienen. Aus Sicherheitsgründen sollte dies eher nicht der Web-Server sein. Dazu aber später mehr.
Benachrichtigung registrieren
Jetzt wird es spannend. Bei Klick auf den Ja-Button wird die Funktion pushRegister() aufgerufen. Diese ist in unserer default.js zu hinterlegen.
Hiermit registrieren wir den Website-Besucher beim Service Worker und weisen ihm eine eindeutige Kennung zu. Diese bilden wir anhand des öffentlichen VAPID-Schlüssels, den wir mittels urlBase64ToUint8Array() konvertieren müsssen, da Google Chrome sonst an der 64-Bit-Codierung scheitern würde.
default.js// Web Push Notification: Key Encoding
function urlBase64ToUint8Array(base64String) {
let padding = '='.repeat((4 - base64String.length % 4) % 4);
let base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
let rawData = window.atob(base64);
let outputArray = new Uint8Array(rawData.length);
for(let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
Die generierte Kennung wird an die register.php übergeben. Dazu später mehr.
Schliesslich senden wir gleich mal eine Willkommens-Nachricht über die Funktion pushNotify().
default.js// Web Push Notification: Simple Notify
function pushNotify(body) {
const title = 'Meine Website';
const icon = 'https://www.boeni.com/includes/images/favicon512.png';
const url = 'https://www.boeni.com';
if(!('Notification' in window)) {
console.log('Web browser does not support desktop notification');
}
if(Notification.permission === 'granted') {
const notification = new Notification(title, {
icon: icon,
body: body,
});
notification.onclick = function() {
window.open(url);
};
setTimeout(function() {
notification.close();
}, 5000);
}
}
Die Konstanten title, icon und url solltest du noch anpassen.
Kennung speichern
Damit wir später Nachrichten senden können, muss die oben generierte Kennung gespeichert werden. Diese Aufgabe übernimmt ein PHP-Script.
register.php<?php
if(isset($_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');
}
header('Content-Type: application/json; charset=utf-8');
$messages = register();
echo json_encode($messages);
function register() {
// Set defaults
$email = 'ich@example.com';
$subject = 'push notification register';
$msg = '';
$success = array();
$warning = array();
$error = array();
// Make sure that it is a POST request
if(strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') != 0){
$msg = 'Request method must be POST';
mail($email, $subject, $msg);
array_push($error, $msg);
return(array($success, $warning, $error));
}
// Get the JSON data from the input stream
$json = trim(file_get_contents('php://input'));
// Check JSON
if(empty(json_decode($json))){
$msg = 'JSON is invalid ('.json_last_error_msg().') '.$json;
mail($email, $subject, $msg);
array_push($error, $msg);
return(array($success, $warning, $error));
}
// Write JSON to file
$obj = json_decode($json, true);
$auth = $obj['subscription']['keys']['auth'];
$filename = $auth.'.json';
$file = fopen('register/'.$filename, 'w');
fwrite($file, $json);
fclose($file);
// Check File
if(is_file('register/'.$filename)) {
$msg = $auth.' registered.';
mail($email, $subject, $msg);
} else {
$msg = $filename.' not saved.';
mail($email, $subject, $msg);
}
array_push($error, $msg);
return(array($success, $warning, $error));
}
?>
In der Variable $email solltest du noch deine E-Mail-Adresse hinterlegen. Dadurch wirst du per E-Mail informiert, falls etwas schief geht oder wenn sich jemand neu registriert.
Man beachte die Access-Control-Headers. Sie müssen für CORS gesetzt werden, damit wir nicht in einen HTTP-500-Error laufen.
Das PHP-Script schreibt die Kennung in eine JSON-Datei im Unterordner register. Den musst du zuerst anlegen.
Service Worker einrichten
Damit wir später Nachrichten senden können, muss der Service Worker natürlich wissen, was er tun muss.
service-worker.js// Register event listener for the notification push event
self.addEventListener('push', e => {
console.log('Service Worker: Push Notification Send');
const title = 'Meine Website';
const icon = 'https://www.boeni.com/includes/images/favicon512.png';
const body = e.data.text() || 'Web Push Notification from Service Worker';
// Keep the service worker alive until the notification is created
e.waitUntil(
self.registration.showNotification(title, {
icon: icon,
body: body
})
);
});
// Register event listener for the notification click event
self.addEventListener('notificationclick', e => {
console.log('Service Worker: Push Notification Click');
e.notification.close();
// This looks to see if the current is already open and focuses if it is
e.waitUntil(
clients
.matchAll({
type: 'window',
})
.then((clientList) => {
for (const client of clientList) {
if (client.url === '/' && 'focus' in client) return client.focus();
}
if (clients.openWindow) return clients.openWindow('/');
}),
);
});
Die Konstanten title und icon solltest du noch anpassen.
Damit ist seitens des Web-Servers alles eingerichtet, um Push-Benachrichtigungen zu versenden.
Die gesamte service-worker.js und default.js kannst du direkt herunterladen, falls du keine Lust hast, die obigen Code-Schnipsel zusammenzusetzen.
Push-Server einrichten
Der Push-Server verwendet web-push, um Nachrichten zu senden. Er sollte idealerweise nicht derselbe Rechner wie der Web-Server sein.
Ich habe mich für die npm-Variante entschieden und diese global an meinem Rechner installiert:
npm install web-push -g
Damit hast du bereits einen funktionierenden Push-Server.
Push-Nachricht senden
Zurück zur register.php. Diese hat wie erwähnt pro registrierenden Besucher im Unterordner register eine json-Datei angelegt. Diese Dateien solltest du nun auf deinen Push-Server kopieren.
scp -p webserver:/includes/register/*.json .
Mit folgenden Befehl kannst du schliesslich eine Benachrichtigung senden:
Die Werte endpoint, key und auth entimmst du der json-Datei. Als vapid-subject hinterlegst du deine E-Mail-Adresse. Und die öffentlichen und privaten VAPID-Schlüssel hast du dir ja bereits erstellt.
Local Storage
Falls du meine vorgeschlagende Local Storage-Einbindung nutzen willst, kannst du die Subscription auch dort hinterlegen, wo sie mit dem Server automatisch synchronisiert wird. Hierfür musst du den gesamten Abschnitt fetch('/includes/register.php', { ... }); mit der Zeile createUserCredentials('push_subscription', subscription); ersetzen.
Variante mit Klassenmodul
Damit ist alles getan. Falls du jedoch vermeiden willst, dass die default.js überbordet, sollten eher seltener benötigte Aktionen in Klassenmodule ausgelagert werden. Klassenmodule müssen bei Bedarf in der default.js importiert werden.
default.js// Load Module
async function loadModule(module) {
const file = './modules/' + module.charAt(0).toLowerCase() + module.slice(1) + '.js';
const mod = await import(file);
return new mod[module];
}
Ich sammle die Klassenmodule im Unterordner modules und nenne die Dateien wie die Klasse, aber mit Kleinbuchstaben beginnend. Die Klasse PushNotification befindet sich also in modules/pushNotification.js. Du kannst diese Datei gerne ebenso einbinden.
In der default.js ist die Funktion pushRegister() anzupassen.
default.js// Web Push Notification Register
async function pushRegister() {
const PushNotification = await loadModule('PushNotification');
const pushResponse = await PushNotification.getSubscription();
const UserCredentials = await loadModule('UserCredentials');
const credentialsResponse = await UserCredentials.getValue('push_subscription');
let msg = 'Du wirst weiterhin Benachrichtigungen erhalten.';
if (pushResponse.subscription === undefined || credentialsResponse.value === undefined) {
PushNotification.register();
msg = 'Du wirst nun Benachrichtigungen erhalten.';
}
PushNotification.notify(msg);
writeToast('success', msg);
}
Die letzte Zeile writeToast('success', msg); kannst du weg lassen, falls du keine Toast-Benachrichtigungen implementiert hast.
Frontend für Push-Server
Der Push-Server verwendet die Anwendung web-push, um Nachrichten zu senden. Aus Sicherheitsgründen sollte diese nicht ebenfalls am Web-Server installiert werden, sondern auf einem Rechner im Intranet. Daher ist ein Web-Frontend für den Push-Server auch nur im Intranet sinnvoll.
Dran bleiben
Du hast es geschafft. Abonniere meine Benachrichtigungen, um weitere News und Anleitungen von mir zu erhalten.