L'API Push permet à une application Web de recevoir des messages envoyés par un serveur, même si l'application Web n'est pas actuellement ouverte dans le navigateur ou ne s'exécute pas sur l'appareil.
- Est-il bien pris en charge?
- Comment ça fonctionne
- Obtenir l'autorisation de l'utilisateur
- Comment fonctionne le côté serveur
- Recevoir un événement Push
L'API Push permet à une application Web de recevoir des messages envoyés par un serveur, même si l'application Web n'est pas actuellement ouverte dans le navigateur ou ne s'exécute pas sur l'appareil
En utilisant l'API Push, vous pouvez envoyer des messages à vos utilisateurs, en les poussant du serveur vers le client, même lorsque l'utilisateur ne navigue pas sur le site.
Cela vous permet de fournir des notifications et des mises à jour de contenu, ce qui vous permet d'avoir un public plus engagé.
C'est énorme car l'un des piliers manquants du Web mobile, par rapport aux applications natives, était la possibilité de recevoir des notifications, ainsi que le support hors ligne.
Est-il bien pris en charge?
L'API Push est un ajout récent aux API du navigateur, et elle est actuellement prise en charge par Chrome (bureau et mobile), Firefox et Opera depuis 2016, Edge depuis la version 17 (début 2018). En savoir plus sur l'état actuel de la prise en charge des navigateurs surhttps://caniuse.com/#feat=push-api
IE ne le prend pas en charge, etSafari a sa propre implémentation.
Étant donné que Chrome et Firefox le prennent en charge, environ 60% des utilisateurs naviguant sur le bureau y ont accès, donc c'est assezen sécuritéutiliser.
Comment ça fonctionne
Aperçu
Lorsqu'un utilisateur visite votre application Web, vous pouvez déclencher un panneau demandant l'autorisation d'envoyer des mises à jour. UNETravailleur de serviceest installé et fonctionnant en arrière-plan écoute unÉvénement push.
Push et Notifications sont un concept et une API distincts, parfois mélangés en raison de lanotifications pushterme utilisé dans iOS. Fondamentalement, l'API Notifications est appelée lorsqu'un événement Push est reçu à l'aide de l'API Push.
Tonserveurenvoie la notification au client et le technicien de service, s'il en a l'autorisation, reçoit unévénement push. Le Service Worker réagit à cet événement endéclencher une notification.
Obtenir l'autorisation de l'utilisateur
La première étape de l'utilisation de l'API Push consiste à obtenir l'autorisation de l'utilisateur pour recevoir des données de votre part.
De nombreux sites implémentent mal ce panneau, le montrant lors du chargement de la première page. L'utilisateur n'est pas encore convaincu que votre contenu est bon et il refusera l'autorisation. Faites-le sagement.
Il y a 6 étapes:
- Vérifiez si les techniciens de service sont pris en charge
- Vérifiez si l'API Push est prise en charge
- Inscrire un technicien de service
- Demander l'autorisation de l'utilisateur
- Abonnez l'utilisateur et obtenez l'objet PushSubscription
- Envoyez l'objet PushSubscription à votre serveur
Vérifiez si les techniciens de service sont pris en charge
if (!('serviceWorker' in navigator)) {
// Service Workers are not supported. Return
return
}
Vérifiez si l'API Push est prise en charge
if (!('PushManager' in window)) {
// The Push API is not supported. Return
return
}
Inscrire un technicien de service
Ce code enregistre le Service Worker situé dans leworker.js
fichier placé à la racine du domaine:
window.addEventListener('load', () => {
navigator.serviceWorker.register('/worker.js')
.then((registration) => {
console.log('Service Worker registration completed with scope: ',
registration.scope)
}, (err) => {
console.log('Service Worker registration failed', err)
})
})
Pour en savoir plus sur le fonctionnement des techniciens de service en détail, consultez leGuide des travailleurs des services.
Demander l'autorisation de l'utilisateur
Maintenant que le service worker est enregistré, vous pouvez demander l'autorisation.
L'API pour ce faire a changé au fil du temps, et elle est passée de l'acceptation d'une fonction de rappel en tant que paramètre au renvoi d'unPromesse, brisant la compatibilité ascendante et descendante, et nous devons fairetous les deuxcar nous ne savons pas quelle approche est mise en œuvre par le navigateur de l'utilisateur.
Le code est le suivant, appelantNotification.requestPermission()
.
const askPermission = () => {
return new Promise((resolve, reject) => {
const permissionResult = Notification.requestPermission((result) => {
resolve(result)
})
if (permissionResult) {
permissionResult.then(resolve, reject)
}
})
.then((permissionResult) => {
if (permissionResult !== 'granted') {
throw new Error('Permission denied')
}
})
}
LepermissionResult
value est une chaîne, qui peut avoir la valeur: -granted
-default
-denied
Ce code amène le navigateur à afficher la boîte de dialogue d'autorisation:
Si l'utilisateur clique sur Bloquer, vous ne pourrez plus demander l'autorisation de l'utilisateur, à moins qu'ils ne débloquent manuellement le site dans un panneau de paramètres avancés du navigateur (très peu probable).
Abonnez l'utilisateur et obtenez l'objet PushSubscription
Si l'utilisateur nous a donné la permission, nous pouvons y souscrire et en appelantregistration.pushManager.subscribe()
.
const APP_SERVER_KEY = 'XXX'
window.addEventListener(‘load’, () => {
navigator.serviceWorker.register(’/worker.js’)
.then((registration) => {
askPermission().then(() => {
const options = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(APP_SERVER_KEY)
}
return registration.pushManager.subscribe(options)
}).then((pushSubscription) => {
// we got the pushSubscription object
}
}, (err) => {
console.log(‘Service Worker registration failed’, err)
})
})
APP_SERVER_KEY
est une chaîne - appeléeClé du serveur d'applicationsouClé VAPID- qui identifie la clé publique de l'application, qui fait partie d'une paire de clés publique / privée.
Il sera utilisé dans le cadre de la validation qui, pour des raisons de sécurité, a lieu pour s'assurer que vous (et seulement vous, pas quelqu'un d'autre) pouvez renvoyer un message push à l'utilisateur.
Envoyez l'objet PushSubscription à votre serveur
Dans l'extrait de code précédent, nous avons obtenu lepushSubscription
object, qui contient tout ce dont nous avons besoin pour envoyer un message push à l'utilisateur. Nous devons envoyer ces informations à notre serveur afin que nous puissions envoyer des notifications plus tard.
Nous créons d'abord une représentation JSON de l'objet
const subscription = JSON.stringify(pushSubscription)
et nous pouvons le publier sur notre serveur en utilisant leRécupérer l'API:
const sendToServer = (subscription) => {
return fetch('/api/subscription', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
})
.then((res) => {
if (!res.ok) {
throw new Error('An error occurred')
}
return res.json()
})
.then((resData) => {
if (!(resData.data && resData.data.success)) {
throw new Error('An error occurred')
}
})
}
sendToServer(subscription)
Côté serveur, le/api/subscription
endpoint reçoit la demande POST et peut stocker les informations d'abonnement dans son stockage.
Comment fonctionne le côté serveur
Jusqu'à présent, nous n'avons parlé que de la partie côté client: obtenir la permission d'un utilisateur d'être notifié à l'avenir.
Et le serveur? Que doit-il faire et comment doit-il interagir avec le client?
Ces exemples côté serveur utilisent Express.js (http://expressjs.com/) comme framework HTTP de base, mais vous pouvez écrire un gestionnaire d'API Push côté serveur dans n'importe quel langage ou framework
Enregistrer un nouvel abonnement client
Lorsque le client envoie un nouvel abonnement, rappelez-vous que nous avons utilisé le/api/subscription
Point de terminaison HTTP POST, envoi des détails de l'objet PushSubscription au format JSON, dans le corps.
Nous initialisons Express.js:
const express = require('express')
const app = express()
Cette fonction utilitaire s'assure que la demande est valide, a un corps et une propriété de point de terminaison, sinon elle renvoie une erreur au client:
const isValidSaveRequest = (req, res) => {
if (!req.body || !req.body.endpoint) {
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
}
La fonction utilitaire suivante enregistre l'abonnement à la base de données, renvoyant une promesse résolue lorsque l'insertion est terminée (ou a échoué). LeinsertToDatabase
function est un espace réservé, nous n'entrerons pas dans ces détails ici:
const saveSubscriptionToDatabase = (subscription) => {
return new Promise((resolve, reject) => {
insertToDatabase(subscription, (err, id) => {
if (err) {
reject(err)
return
}
<span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">id</span>)
})
})
}
Nous utilisons ces fonctions dans le gestionnaire de requêtes POST ci-dessous. On vérifie si la demande est valide, puis on enregistre la demande puis on retourne undata.success: true
réponse au client, ou une erreur:
app.post('/api/subscription', (req, res) => {
if (!isValidSaveRequest(req, res)) {
return
}
saveSubscriptionToDatabase(req, res.body)
.then((subscriptionId) => {
res.setHeader(‘Content-Type’, ‘application/json’)
res.send(JSON.stringify({ data: { success: true } }))
})
.catch((err) => {
res.status(500)
res.setHeader(‘Content-Type’, ‘application/json’)
res.send(JSON.stringify({
error: {
id: ‘unable-to-save-subscription’,
message: ‘Subscription received but failed to save it’
}
}))
})
})
app.listen(3000, () => {
console.log(‘App listening on port 3000’)
})
Envoi d'un message Push
Maintenant que le serveur a enregistré le client dans sa liste, nous pouvons lui envoyer des messages Push. Voyons comment cela fonctionne en créant un exemple d'extrait de code qui récupère tous les abonnements et leur envoie un message Push à tous en même temps.
Nous utilisons une bibliothèque parce que leProtocole Web Pushestcomplexe, et une bibliothèque nous permet d'abstraire un grand nombre de code de bas niveau qui nous permet de travailler en toute sécurité et de gérer correctement n'importe quel cas de bord.
Cet exemple utilise le
web-push
Node.jsbibliothèque (https://github.com/web-push-libs/web-push) pour gérer l'envoi du message Push
Nous initialisons d'abord leweb-push
lib, et nous générons un tuple de clés privées et publiques, et les définissons comme détails VAPID:
const webpush = require('web-push')
const vapidKeys = webpush.generateVAPIDKeys()
const PUBLIC_KEY = ‘XXX’
const PRIVATE_KEY = ‘YYY’
const vapidKeys = {
publicKey: PUBLIC_KEY,
privateKey: PRIVATE_KEY
}
webpush.setVapidDetails(
‘mailto:[email protected]’,
vapidKeys.publicKey,
vapidKeys.privateKey
)
Ensuite, nous avons mis en place untriggerPush()
, responsable de l'envoi de l'événement push à un client. Il appelle justewebpush.sendNotification()
et attrape toute erreur. Si le code d'état HTTP d'erreur de retour est410, ce qui signifiedisparu, nous supprimons cet abonné de la base de données.
const triggerPush = (subscription, dataToSend) => {
return webpush.sendNotification(subscription, dataToSend)
.catch((err) => {
if (err.statusCode === 410) {
return deleteSubscriptionFromDatabase(subscription._id)
} else {
console.log('Subscription is no longer valid: ', err)
}
})
}
Nous n'implémentons pas l'obtention des abonnements à partir de la base de données, mais nous la laissons comme un stub:
const getSubscriptionsFromDatabase = () => {
//stub
}
La viande du code est le rappel de la requête POST au/api/push
point final:
app.post('/api/push', (req, res) => {
return getSubscriptionsFromDatabase()
.then((subscriptions) => {
let promiseChain = Promise.resolve()
for (let i = 0; i < subscriptions.length; i++) {
const subscription = subscriptions[i]
promiseChain = promiseChain.then(() => {
return triggerPush(subscription, dataToSend)
})
}
return promiseChain
})
.then(() => {
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({ data: { success: true } }))
})
.catch((err) => {
res.status(500)
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({
error: {
id: 'unable-to-send-messages',
message: `Failed to send the push ${err.message}`
}
}))
})
})
Ce que fait le code ci-dessus est: il obtient tous les abonnements de la base de données, puis il les itère, et il appelle letriggerPush()
fonction que nous avons expliqué auparavant.
Une fois les abonnements terminés, nous renvoyons une réponse JSON réussie, à moins qu'une erreur ne se produise et nous renvoyons une erreur 500.
Dans le monde réel…
Il est peu probable que vous configuriez votre propre serveur Push à moins que vous n'ayez un cas d'utilisation très spécial, ou que vous souhaitiez simplement apprendre la technologie ou que vous aimiez faire du bricolage. Au lieu de cela, vous souhaitez généralement utiliser des plates-formes telles que OneSignal (https://onesignal.com) qui gèrent de manière transparente les événements Push vers tous les types de plates-formes, Safari et iOS inclus, gratuitement.
Recevoir un événement Push
Lorsqu'un événement Push est envoyé depuis le serveur, comment le client l'obtient-il?
C'est normalJavaScriptauditeur d'événements, sur lepush
événement, qui s'exécute dans un Service Worker:
self.addEventListener('push', (event) => {
// data is available in event.data
})
event.data
contient lePushMessageData
objet qui expose des méthodes pour récupérer les données push envoyées par le serveur, au format souhaité:
- arrayBuffer (): comme un
ArrayBuffer
objet - goutte(): comme unGoutteobjet
- json (): analysé commeJSON
- texte(): texte brut
Vous utiliserez normalementevent.data.json()
.
Afficher une notification
Ici, nous croisons un peu avec leAPI de notifications, mais pour une bonne raison, car l'un des principaux cas d'utilisation de l'API Push est d'afficher des notifications.
À l'intérieur de notrepush
écouteur d'événement dans le Service Worker, nous devons afficher la notification à l'utilisateur et indiquer à l'événement d'attendre que le navigateur l'ait affiché avant que la fonction puisse se terminer. Nous prolongons la durée de vie de l'événement jusqu'à ce que le navigateur ait fini d'afficher la notification (jusqu'à ce que la promesse soit résolue), sinon le Service Worker pourrait être arrêté au milieu de votre traitement:
self.addEventListener('push', (event) => {
const promiseChain = self.registration.showNotification('Hey!')
event.waitUntil(promiseChain)
})
En savoir plus sur les notifications dans leGuide de l'API Notifications.
Téléchargez mon gratuitManuel du débutant JavaScript
Plus de didacticiels sur le navigateur:
- Quelques astuces utiles disponibles en HTML5
- Comment j'ai fait fonctionner un site Web basé sur un CMS hors ligne
- Le guide complet des applications Web progressives
- L'API Fetch
- Le guide de l'API Push
- L'API Channel Messaging
- Tutoriel pour les techniciens de service
- Le guide de l'API Cache
- Le guide de l'API de notification
- Plongez dans IndexedDB
- L'API Selectors: querySelector et querySelectorAll
- Chargez efficacement JavaScript avec différé et asynchrone
- Le modèle d'objet de document (DOM)
- L'API Web Storage: stockage local et stockage de session
- Découvrez comment fonctionnent les cookies HTTP
- L'API History
- Le format d'image WebP
- XMLHttpRequest (XHR)
- Un tutoriel SVG approfondi
- Que sont les URL de données
- Feuille de route pour apprendre la plate-forme Web
- CORS, partage de ressources inter-origines
- Travailleurs Web
- Le guide requestAnimationFrame ()
- Quel est le Doctype
- Utilisation de la console DevTools et de l'API de la console
- L'API de synthèse vocale
- Comment attendre l'événement DOM ready en JavaScript brut
- Comment ajouter une classe à un élément DOM
- Comment faire une boucle sur des éléments DOM à partir de querySelectorAll
- Comment supprimer une classe d'un élément DOM
- Comment vérifier si un élément DOM a une classe
- Comment modifier la valeur d'un nœud DOM
- Comment ajouter un événement de clic à une liste d'éléments DOM renvoyés par querySelectorAll
- WebRTC, l'API Web en temps réel
- Comment obtenir la position de défilement d'un élément en JavaScript
- Comment remplacer un élément DOM
- Comment accepter uniquement les images dans un champ de fichier d'entrée
- Pourquoi utiliser une version préliminaire d'un navigateur?
- L'objet Blob
- L'objet fichier
- L'objet FileReader
- L'objet FileList
- ArrayBuffer
- ArrayBufferView
- L'objet URL
- Tableaux typés
- L'objet DataView
- L'API BroadcastChannel
- L'API Streams
- L'objet FormData
- L'objet Navigateur
- Comment utiliser l'API de géolocalisation
- Comment utiliser getUserMedia ()
- Comment utiliser l'API Drag and Drop
- Comment travailler avec le défilement sur les pages Web
- Gestion des formulaires en JavaScript
- Événements de clavier
- Événements de souris
- Événements tactiles
- Comment supprimer tous les enfants d'un élément DOM
- Comment créer un attribut HTML à l'aide de JavaScript vanille
- Comment vérifier si une case est cochée à l'aide de JavaScript?
- Comment copier dans le presse-papiers à l'aide de JavaScript
- Comment désactiver un bouton à l'aide de JavaScript
- Comment rendre une page modifiable dans le navigateur
- Comment obtenir des valeurs de chaîne de requête en JavaScript avec URLSearchParams
- Comment supprimer tous les CSS d'une page à la fois
- Comment utiliser insertAdjacentHTML
- Safari, prévenez avant de quitter
- Comment ajouter une image au DOM en utilisant JavaScript
- Comment réinitialiser un formulaire
- Comment utiliser Google Fonts