Le guide de l'API 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.

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:

  1. Vérifiez si les techniciens de service sont pris en charge
  2. Vérifiez si l'API Push est prise en charge
  3. Inscrire un technicien de service
  4. Demander l'autorisation de l'utilisateur
  5. Abonnez l'utilisateur et obtenez l'objet PushSubscription
  6. 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.jsfichier 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')
    }
  })
}

LepermissionResultvalue 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:

The browser permission dialogue

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_KEYest 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 lepushSubscriptionobject, 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/subscriptionendpoint 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/subscriptionPoint 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é). LeinsertToDatabasefunction 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: trueré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 leweb-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-pushlib, 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/pushpoint 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.datacontient lePushMessageDataobjet qui expose des méthodes pour récupérer les données push envoyées par le serveur, au format souhaité:

  • arrayBuffer (): comme unArrayBufferobjet
  • 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: