Pousser une notification à une utilisateur de notre application permet de l’informer qu’un évènement pouvant l’intéresser s’est produit dans l’application (il a reçu un message, un nouvel article a été publié, …) c’est donc un bon moyen de l’inciter à revenir sur l’application et de continuer à l’utiliser. Voyons comment faire cela dans une Progressive Web App
Pour en savoir plus sur les PWA, vous pouvez consulter les articles précédents sur le sujet :
Voici les différentes étapes pour réaliser l'envoi d'une notification push
1. L’utilisateur choisi de s’abonner à des notifications et le navigateur s’inscrit auprès d’un serveur de messagerie push
2. Le navigateur transmet les infos de cette inscription au serveur applicatif qui les enregistre
3. Lorsque le serveur applicatif souhaite notifier un utilisateur, il envoie un message push encrypté au serveur de messagerie associé à l’utilisateur concerné.
4. Le serveur de message envoie un push au service worker du navigateur de l’utilisateur
5. Le service worker de l’application affiche une notification.
L’envoi de messages push via un serveur de messagerie se fait grâce au protocole WebPush.
Les messages WebPush sont encryptés, la première chose à faire pour permettre à une application d’envoyer des notifications est de générer un couple clé privé / clé publique pour cette encryptions.
Ces clés permettent également au serveur de messagerie d’identifier le serveur web envoyant un message push. C’est le protocole Volontary Application Server Identification ou VAPID. Ce couple de clé publique et clé privé est appelé VAPID keys. Cela permet de s’assurer qu’une autre application ne peut pas envoyer des notifications à nos utilisateurs sans connaitre la clé privée de notre application.
On peut trouver des librairies open sources pour utiliser le protocole web push dans la plupart des langage (https://github.com/web-push-libs/).
Pour l’utiliser en C#, on peut installer le package nuget WebPush
Install-Package WebPush
Cette librairie contient une méthode permettant de générer les VAPID keys :
VapidDetails vapidKeys = VapidHelper.GenerateVapidKeys();
Console.WriteLine($"clé publique {vapidKeys.PublicKey}, clé privée :{vapidKeys.PrivateKey}");
Pour qu’une application puisse afficher des notifications, il faut que l’utilisateur autorise cette application à le faire. On commence donc par vérifier que l’utilisateur à bien accepté l’affichage de notifications :
async function subscribe() {
if (Notification.permission !== "granted")
if (await Notification.requestPermission() === "denied") {
console.warn("L'utilisateur n'a pas autorisé les notifications");
return null;
}
…
}
Pour s’abonner à des messages push, on va utiliser l’API PushManager du service worker (https://developer.mozilla.org/en-US/docs/Web/API/PushManager)
const serviceWorker = await navigator.serviceWorker.ready;
const subscription = await serviceWorker.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(window.publicKey) });
let keys = subscription.toJSON().keys;
Il faut ensuite qu’on envoie les informations de cet abonnement à notre server web pour qu’il l’enregistre :
await fetch('/api/Subscription/Subscribe', {
method: "POST",
body: JSON.stringify({
endpoint: subscription.endpoint,
auth: keys.auth,
p256dh: keys.p256dh,
})
}
);
Un abonnement est composé de deux clés identifiant l’utilisateur (auth et p256dh), ainsi que d’un endpoint qui est l’url du serveur de messagerie ayant créé cet abonnement et pouvant lui envoyer des messages webpush. Ces serveurs de messageries sont gérés par les éditeurs des navigateurs (Mozilla, Google, Microsoft, …) Il faut donc enregistrer ce triplet d’informations pour chaque abonnement.
Pour s’abonner à des messages push, il faut transmettre au serveur de messagerie la clé publique VAPID sous forme de tableau d’entiers non signés. Malheureusement l’API javascript ne contient pas de fonction permettant de transformer une chaine vers un tableau d’entier, on a donc besoin de coder nous même la fonction le faisant.
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/')
;
const rawData = window.atob(base64);
return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
}
Lorsque l’on souhaite envoyer une notification à un utilisateur, il faut commencer par récupérer les informations de sont abonnement qu’on a enregistré précédemment.
Le package nuget WebPush permet d’envoyer des messages à un utilisateur via un serveur de messagerie. Un message webpush contient :
public async Task SendWebPush(string endpoint, string p256dh, string auth, Article blogPost)
{
var subscription = new PushSubscription(endpoint, p256dh, auth);
var vapidDetails = new VapidDetails("mailto:admin@test.com", PublicKey, PrivateKey);
var webPushClient = new WebPushClient();
var payload = JsonConvert.SerializeObject(blogPost);
try
{
await webPushClient.SendNotificationAsync(subscription, payload, vapidDetails);
}
catch (WebPushException exception)
{
if (exception.StatusCode == System.Net.HttpStatusCode.Gone)
await DeleteSubscription(endpoint, p256dh, auth);
}
}
Le serveur de messagerie peut nous renvoyer plusieurs types d’erreurs (si le message est mal formaté, ou est trop gros). Il y un type d’erreur qu'il est conseillé de gérer, c’est le cas ou l’utilisateur s’est désabonné des notifications de notre application. Dans ce cas le serveur de messagerie nous renvoie un code HTTP 410, il faut donc supprimer cet abonnement de notre base de données pour éviter de retenter d’y envoyer des messages WebPush.
Une fois notre push envoyé au serveur de messagerie, celui-ci va essayer de l’envoyer à l’utilisateur concerné. (cela peut prendre quelques minutes suivant la charge des serveurs de messagerie au moment de l’envoie). Si l’utilisateur n’est pas disponible (son appareil n’est pas connecté, le navigateur est fermé, …) le serveur de messagerie ressayera d'envoyer le push périodiquement pendant environ 24 heures.
On a vu que le service worker servait de proxy entre une application web et internet en interceptant les requêtes sortantes. C’est lui aussi qui va recevoir les messages entrants.
Un des gros intérêts que cela apporte est qu’un service worker continue d'être exécuté dans le navigateur de l’utilisateur même si celui-ci à fermé notre application. Une PWA peut donc afficher des notifications sans être ouverte par l'utilisateur.
Pour gérer la réception un message webPush, il faut s’abonner à l’évènement push dans le service worker. Dans cet évenement, on peut afficher une notification lors de la réception d’un webPush via la méthode showNotification (https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification)
self.addEventListener("push", (event: PushEvent) => {
const pushData = event.data.json();
event.waitUntil(
self.registration.showNotification("LesDieuxDuCode", {
body: pushData.Summary,
dir: "ltr",
tag: "lesdieuxducode",
icon: "/images/logo.png",
badge: "/images/logo.png",
image: pushData.TitleImage,
data: pushData
})
);
});
On passe en paramètre à cette méthode, le titre de la notification et des options :
Chaque navigateur utilise ces options utilise ces options pour son propre affichage de la notification. Le rendu de cette notification dépendra donc du navigateur ayant installé la PWA.
Notification sous Firefox sur Windows 10
Notification sous Chrome sur Windows 10
Notification sous Chrome sur Android 8
Lorsque l’utilisateur click sur la notification, cela déclenche l’événement notificationclick du service worker. Généralement dans ce événement on va vouloir ouvrir une page de notre PWA.
self.addEventListener("notificationclick", (event: any) => {
const pushData = event.notification.data;
event.notification.close();
self.clients.openWindow(`/blog/${pushData.articleId}`)
});
Le protocole webpush et l'affichage de notification est maintenant supporté par tous les navigateurs modernes.
La possibilité d'afficher des notifications est une des grande fonctionnalités des navigateurs ayant rendu les PWA possibles
Commentaires :
Aucun commentaires pour le moment
Laissez un commentaire :