Comment envoyer des notifications Web Push à partir d’applications Django

L’auteur a sélectionné le Open Internet / Free Speech Fund pour recevoir un don dans le cadre du https://do.co/w4do-cta Programme [Write for DOnations] .

introduction

Le Web évolue constamment et il peut désormais offrir des fonctionnalités qui n’étaient auparavant disponibles que sur les appareils mobiles natifs. L’introduction de JavaScript service worker a donné au Web de nouvelles possibilités d’effectuer des tâches telles que la synchronisation en arrière-plan, la mise en cache hors connexion et l’envoi de https: // développeur. .mozilla.org / fr / docs / Web / API / Push_API [notifications push].

Les notifications push permettent aux utilisateurs de choisir de recevoir des mises à jour d’applications mobiles et Web. Ils permettent également aux utilisateurs de reprendre contact avec des applications existantes en utilisant un contenu personnalisé et pertinent.

Dans ce tutoriel, vous allez configurer une application Django sur Ubuntu 18.04 qui envoie des notifications push chaque fois qu’une activité nécessite la visite de l’utilisateur par l’application. Pour créer ces notifications, vous utiliserez le package Django-Webpush, puis configurerez et enregistrez un agent de service afin d’afficher des notifications au client. L’application de travail avec notifications ressemblera à ceci:

image: https: //assets.digitalocean.com/articles/django_push_18_04/web_push_final.png [Web push final]

Conditions préalables

Avant de commencer ce guide, vous aurez besoin des éléments suivants:

Étape 1 - Installer Django-Webpush et obtenir les clés Vapid

Django-Webpush est un package qui permet aux développeurs d’intégrer et d’envoyer des notifications Web push dans les applications Django. Nous allons utiliser ce paquet pour déclencher et envoyer des notifications push à partir de notre application. Au cours de cette étape, vous installerez Django-Webpush et obtiendrez les clés _VAPID (identification volontaire du serveur d’applications) _ nécessaires pour identifier votre serveur et garantir l’unicité de chaque demande.

Assurez-vous de vous trouver dans le répertoire de projet `+ ~ / + + que vous avez créé dans les conditions préalables:

cd ~/

Activez votre environnement virtuel:

source /bin/activate

Mettez à niveau votre version de + pip + pour vous assurer qu’elle est à jour:

pip install --upgrade pip

Installez Django-Webpush:

pip install django-webpush

Après avoir installé le paquet, ajoutez-le à la liste des applications de votre fichier + settings.py +. Commencez par ouvrir + settings.py +:

nano ~/djangopush/djangopush/settings.py

Ajoutez + webpush + à la liste de + INSTALLED_APPS +:

~ / djangopush / djangopush / settings.py

...

INSTALLED_APPS = [
   ...

]
...

Enregistrez le fichier et quittez votre éditeur.

Exécutez migrations sur l’application pour appliquer les modifications que vous avez apportées au schéma de votre base de données:

python manage.py migrate

La sortie ressemblera à ceci, indiquant une migration réussie:

OutputOperations to perform:
 Apply all migrations: admin, auth, contenttypes, sessions, webpush
Running migrations:
 Applying webpush.0001_initial... OK

La prochaine étape de la configuration des notifications Web push consiste à obtenir les clés VAPID. Ces clés identifient le serveur d’application et peuvent être utilisées pour réduire la confidentialité des URL d’abonnement Push car elles limitent les abonnements à un serveur spécifique.

Pour obtenir des clés VAPID, accédez à l’application Web wep-push-codelab. Ici, vous recevrez des clés générées automatiquement. Copiez les clés privées et publiques.

Ensuite, créez une nouvelle entrée dans + settings.py + pour vos informations VAPID. Tout d’abord, ouvrez le fichier:

nano ~/djangopush/djangopush/settings.py

Ensuite, ajoutez une nouvelle directive appelée + WEBPUSH_SETTINGS + avec vos clés publique et privée VAPID et votre adresse électronique en dessous de + AUTH_PASSWORD_VALIDATORS +:

~ / djangopush / djangopush / settings.py

...

AUTH_PASSWORD_VALIDATORS = [
   ...
]







# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

...

N’oubliez pas de remplacer les valeurs de substitution «», «» et «++» par vos propres informations. Votre adresse e-mail indique comment vous serez averti si le serveur Push rencontre des problèmes.

Nous allons ensuite configurer des vues qui afficheront la page d’accueil de l’application et déclencheront des notifications push pour les utilisateurs abonnés.

Étape 2 - Configuration des vues

Dans cette étape, nous allons configurer une base + home + view avec https://docs.djangoproject.com/en /2.1/ref/request-response/#django.http.HttpRequest [+ HttpResponse + objet de réponse] pour notre page d’accueil, avec une vue + send_push +. Les vues sont des fonctions qui renvoient des objets de réponse à partir de requêtes Web. La vue + send_push + utilisera la bibliothèque Django-Webpush pour envoyer des notifications push contenant les données saisies par un utilisateur sur la page d’accueil.

Accédez au dossier + ~ / djangopush / djangopush +:

cd ~/djangopush/djangopush

Exécuter + ls + dans le dossier vous montrera les fichiers principaux du projet:

Output/__init__.py
/settings.py
/urls.py
/wsgi.py

Les fichiers de ce dossier sont générés automatiquement par l’utilitaire + django-admin + que vous avez utilisé pour créer votre projet dans les conditions préalables. Le fichier + settings.py + contient des configurations pour l’ensemble du projet, telles que les applications installées et le dossier racine statique. Le fichier + urls.py + contient les configurations d’URL pour le projet. C’est ici que vous allez configurer des itinéraires pour correspondre aux vues que vous avez créées.

Créez un nouveau fichier dans le répertoire + ~ / djangopush / djangopush + appelé + views.py +, qui contiendra les vues de votre projet:

nano ~/djangopush/djangopush/views.py

La première vue que nous allons créer est la vue + home +, qui affiche la page d’accueil où les utilisateurs peuvent envoyer des notifications push. Ajoutez le code suivant au fichier:

~ / djangopush / djangopush / views.py

from django.http.response import HttpResponse
from django.views.decorators.http import require_GET

@require_GET
def home(request):
   return HttpResponse('<h1>Home Page<h1>')

La vue + home + est décorée par le décorateur + require_GET +, ce qui limite la vue aux requêtes GET. Une vue renvoie généralement une réponse pour chaque demande qui lui est faite. Cette vue renvoie une simple balise HTML en réponse.

La vue suivante que nous allons créer est + send_push +, qui gérera les notifications push envoyées à l’aide du paquetage + django-webpush +. Il sera limité aux demandes POST et sera exempté de la protection Cross Site Request Forgery (CSRF). Cela vous permettra de tester l’affichage à l’aide de Postman ou de tout autre service RESTful. En production, toutefois, vous devez supprimer ce décorateur pour éviter de rendre vos vues vulnérables à CSRF.

Pour créer la vue + send_push +, ajoutez d’abord les importations suivantes pour activer les réponses JSON et accédez à la fonction + send_user_notification + dans la bibliothèque + webpush +:

~ / djangopush / djangopush / views.py

from django.http.response import , HttpResponse
from django.views.decorators.http import require_GET,

Ensuite, ajoutez le décorateur + require_POST +, qui utilisera le corps de requête envoyé par l’utilisateur pour créer et déclencher une notification push:

~ / djangopush / djangopush / views.py

@require_GET
def home(request):
   ...


@require_POST
@csrf_exempt
def send_push(request):
   try:
       body = request.body
       data = json.loads(body)

       if 'head' not in data or 'body' not in data or 'id' not in data:
           return JsonResponse(status=400, data={"message": "Invalid data format"})

       user_id = data['id']
       user = get_object_or_404(User, pk=user_id)
       payload = {'head': data['head'], 'body': data['body']}
       send_user_notification(user=user, payload=payload, ttl=1000)

       return JsonResponse(status=200, data={"message": "Web push successful"})
   except TypeError:
       return JsonResponse(status=500, data={"message": "An error occurred"})

Nous utilisons deux décorateurs pour la vue + send_push +: le décorateur + require_POST +, qui limite la vue aux demandes POST, et le décorateur + csrf_exempt +, qui exempte la vue de la protection CSRF.

Cette vue attend les données POST et effectue les opérations suivantes: elle obtient le package + body + de la demande et, à l’aide du package json, désérialise le document JSON. vers un objet Python à l’aide de https://docs.python.org/3.6/library/json.html [+ json.loads +]. + json.loads + prend un document JSON structuré et le convertit en objet Python.

La vue s’attend à ce que l’objet de corps de requête ait trois propriétés:

  • + head +: Le titre de la notification push.

  • + body +: le corps de la notification.

  • + id +: Le + id + de l’utilisateur de la requête.

Si l’une des propriétés requises est manquante, la vue renverra un + JSONResponse + avec le statut 404 «Introuvable». Si l’utilisateur avec la clé primaire donnée existe, la vue renverra le + utilisateur + avec la clé primaire correspondante en utilisant le https://docs.djangoproject.com/en/2.1/topics/http/shortcuts/#get-object -or-404 [+ get_object_or_404 + fonction]] de la bibliothèque + django.shortcuts + `. Si l’utilisateur n’existe pas, la fonction renverra une erreur 404.

La vue utilise également la fonction + send_user_notification + de la bibliothèque + webpush +. Cette fonction prend trois paramètres:

  • + User +: le destinataire de la notification push.

  • + payload +: les informations de notification, qui incluent les notifications + head + et + body +.

  • + ttl +: temps maximum, en secondes, pendant lequel la notification doit être stockée si l’utilisateur est hors ligne.

Si aucune erreur ne se produit, la vue retourne un + JSONResponse + avec un statut 200 «Success» et un objet de données. Si un «+ KeyError » se produit, la vue renverra un statut 500 «Erreur interne du serveur». Un " KeyError +" se produit lorsque la clé demandée d’un objet n’existe pas.

Dans l’étape suivante, nous allons créer les itinéraires d’URL correspondants pour correspondre aux vues que nous avons créées.

Étape 3 - Mappage des URL sur des vues

Django permet de créer des URLs qui se connectent aux vues avec un module Python appelé + URLconf +. Ce module mappe les expressions de chemin d’URL avec les fonctions Python (vos vues). Généralement, un fichier de configuration d’URL est généré automatiquement lorsque vous créez un projet. Dans cette étape, vous allez mettre à jour ce fichier afin d’inclure de nouvelles routes pour les vues que vous avez créées à l’étape précédente, ainsi que les URL de l’application + django-webpush +, qui fourniront des points de terminaison aux utilisateurs abonnés pour les notifications en mode push.

Pour plus d’informations sur les vues, veuillez consulter Comment créer des vues Django.

Ouvrez + urls.py +:

nano ~/djangopush/djangopush/urls.py

Le fichier ressemblera à ceci:

~ / djangopush / djangopush / urls.py

"""untitled URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
   https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
   1. Add an import:  from my_app import views
   2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
   1. Add an import:  from other_app.views import Home
   2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
   1. Import the include() function: from django.urls import include, path
   2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path

urlpatterns = [
   path('admin/', admin.site.urls),
]

L’étape suivante consiste à mapper les vues que vous avez créées aux URL. Tout d’abord, ajoutez l’importation + include + pour vous assurer que toutes les routes de la bibliothèque Django-Webpush seront ajoutées à votre projet:

~ / djangopush / djangopush / urls.py

"""webpushdjango URL Configuration
...
"""
from django.contrib import admin
from django.urls import path,

Ensuite, importez les vues que vous avez créées à la dernière étape et mettez à jour la liste + urlpatterns + pour mapper vos vues:

~ / djangopush / djangopush / urls.py

"""webpushdjango URL Configuration
...
"""
from django.contrib import admin
from django.urls import path,



urlpatterns = [
                 path('admin/', admin.site.urls),



             ]

Ici, la liste + urlpatterns + enregistre les URL du paquet + django-webpush + et mappe vos vues sur les URL + / send_push + et + / home +.

Testons la vue + / home + pour s’assurer qu’elle fonctionne comme prévu. Assurez-vous que vous êtes dans le répertoire racine du projet:

cd ~/djangopush

Démarrez votre serveur en lançant la commande suivante:

python manage.py runserver :8000

Accédez à + ​​http: //: 8000 +. Vous devriez voir la page d’accueil suivante:

image: https: //assets.digitalocean.com/articles/django_push_18_04/django_push_home.png [Vue de la page d’accueil initiale]

À ce stade, vous pouvez tuer le serveur avec + CTRL + C +, et nous passerons à la création de modèles et à leur rendu dans nos vues à l’aide de la fonction + render +.

Étape 4 - Création de modèles

Le moteur de gabarit de Django vous permet de définir les couches de votre application destinées à l’utilisateur avec des gabarits similaires aux fichiers HTML. Dans cette étape, vous allez créer et rendre un modèle pour la vue + home +.

Créez un dossier appelé + templates + dans le répertoire racine de votre projet:

mkdir ~/djangopush/templates

Si vous exécutez + ls + dans le dossier racine de votre projet à ce stade, le résultat ressemblera à ceci:

Output/djangopush
/templates
db.sqlite3
manage.py
/my_env

Créez un fichier nommé + home.html dans le dossier` + templates`:

nano ~/djangopush/templates/home.html

Ajoutez le code suivant au fichier pour créer un formulaire dans lequel les utilisateurs peuvent entrer des informations afin de créer des notifications push:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <meta name="vapid-key" content="{{ vapid_key }}">
   {% if user.id %}
       <meta name="user_id" content="{{ user.id }}">
   {% endif %}
   <title>Web Push</title>
   <link href="https://fonts.googleapis.com/css?family=PT+Sans:400,700" rel="stylesheet">
</head>

<body>
<div>
   <form id="send-push__form">
       <h3 class="header">Send a push notification</h3>
       <p class="error"></p>
       <input type="text" name="head" placeholder="Header: Your favorite airline 😍">
       <textarea name="body" id="" cols="30" rows="10" placeholder="Body: Your flight has been cancelled 😱😱😱"></textarea>
       <button>Send Me</button>
   </form>
</div>
</body>
</html>

Le + corps + du fichier inclut un formulaire avec deux champs: un élément + input + contiendra l’en-tête / titre de la notification et un élément + textarea + contiendra le corps de la notification.

Dans la section + head du fichier, il y a deux balises` + meta` qui contiendront la clé publique VAPID et l’identifiant de l’utilisateur. Ces deux variables sont nécessaires pour enregistrer un utilisateur et lui envoyer des notifications push. L’identifiant de l’utilisateur est requis ici car vous allez envoyer des requêtes AJAX au serveur et le + id + sera utilisé pour identifier l’utilisateur. Si l’utilisateur actuel est un utilisateur enregistré, le modèle créera une balise + meta avec son` + id` comme contenu.

La prochaine étape consiste à dire à Django où trouver vos modèles. Pour ce faire, vous allez éditer + settings.py + et mettre à jour la liste + TEMPLATE.

Ouvrez le fichier + settings.py +:

nano ~/djangopush/djangopush/settings.py

Ajoutez les éléments suivants à la liste + DIRS + pour spécifier le chemin d’accès au répertoire des modèles:

~ / djangopush / djangopush / settings.py

...
TEMPLATES = [
   {
       'BACKEND': 'django.template.backends.django.DjangoTemplates',
       'DIRS': [],
       'APP_DIRS': True,
       'OPTIONS': {
           'context_processors': [
               ...
           ],
       },
   },
]
...

Ensuite, dans votre fichier + views.py, mettez à jour la vue` + home + pour restituer le modèle + home.html`. Ouvrez le fichier:

nano ~/djangpush/djangopush/views.py

Tout d’abord, ajoutez quelques importations supplémentaires, y compris la configuration + settings +, qui contient tous les paramètres du projet du fichier + settings.py +, et la fonction + render + de + django.shortcuts +:

~ / djangopush / djangopush / views.py

...
from django.shortcuts import , get_object_or_404
...
import json


...

Ensuite, supprimez le code initial que vous avez ajouté à la vue + home + et ajoutez le texte suivant, qui spécifie le rendu du modèle que vous venez de créer:

~ / djangopush / djangopush / views.py

...

@require_GET
def home(request):

Le code affecte les variables suivantes:

  • + webpush_settings +: la valeur de l’attribut + WEBPUSH_SETTINGS + est attribuée à partir de la configuration + settings +.

  • + vapid_key +: Ceci obtient la valeur + VAPID_PUBLIC_KEY + de l’objet + webpush_settings + à envoyer au client. Cette clé publique est vérifiée par rapport à la clé privée pour garantir que le client avec la clé publique est autorisé à recevoir des messages Push du serveur.

  • + utilisateur +: Cette variable provient de la requête entrante. Chaque fois qu’un utilisateur adresse une requête au serveur, les détails de cet utilisateur sont stockés dans le champ + utilisateur +.

Le https://docs.djangoproject.com/fr/2.1/topics/http/shortcuts/#render [+ render + function] renverra un fichier HTML et un https://docs.djangoproject.com/en/2.1 /ref/templates/api/#django.template.Context[context objet] contenant l’utilisateur actuel et la clé publique vapid du serveur. Il faut trois paramètres ici: le request +, le + template + à restituer et l’objet contenant les variables qui seront utilisées dans le template.

Avec notre modèle créé et la vue + home + mise à jour, nous pouvons passer à la configuration de Django pour servir nos fichiers statiques.

Étape 5 - Servir des fichiers statiques

Les applications Web incluent les fichiers CSS, JavaScript et d’autres fichiers image que Django appelle «fichiers statiques». Django vous permet de collecter tous les fichiers statiques de chaque application de votre projet dans un emplacement unique à partir duquel ils sont servis. Cette solution s’appelle + django.contrib.staticfiles +. Dans cette étape, nous mettrons à jour nos paramètres pour indiquer à Django où seront stockés nos fichiers statiques.

Ouvrez + settings.py +:

nano ~/djangopush/djangopush/settings.py

Dans + settings.py +, assurez-vous d’abord que + STATIC_URL + a été défini:

~ / djangopush / djangopush / settings.py

...
STATIC_URL = '/static/'

Ensuite, ajoutez une liste de répertoires nommés + STATICFILES_DIRS + où Django recherchera les fichiers statiques:

~ / djangopush / djangopush / settings.py

...
STATIC_URL = '/static/'

Vous pouvez maintenant ajouter le + STATIC_URL + à la liste des chemins définis dans votre fichier + urls.py +.

Ouvrez le fichier:

nano ~/djangopush/djangopush/urls.py

Ajoutez le code suivant, qui importera la configuration + static + url et mettra à jour la liste + urlpatterns +. La fonction d’aide utilise ici les propriétés + STATIC_URL + et + + STATIC_ROOT + fournies dans le fichier + settings.py + pour servir les fichiers statiques du projet:

~ / djangopush / djangopush / urls.py

...



urlpatterns = [
   ...
]

Avec nos paramètres de fichiers statiques configurés, nous pouvons passer au style de la page d’accueil de l’application.

Étape 6 - Mise en forme de la page d’accueil

Une fois que votre application a été configurée pour gérer les fichiers statiques, vous pouvez créer une feuille de style externe et la lier au fichier + home.html + pour styliser la page d’accueil. Tous vos fichiers statiques seront stockés dans un répertoire + static + dans le dossier racine de votre projet.

Créez un dossier + static et un dossier` + css` dans le dossier + static +:

mkdir -p ~/djangopush/static/css

Ouvrez un fichier CSS appelé + styles.css + dans le dossier + css +:

nano ~/djangopush/static/css/styles.css

Ajoutez les styles suivants pour la page d’accueil:

~ / djangopush / static / css / styles.css

body {
   height: 100%;
   background: rgba(0, 0, 0, 0.87);
   font-family: 'PT Sans', sans-serif;
}

div {
   height: 100%;
   display: flex;
   align-items: center;
   justify-content: center;
}

form {
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   width: 35%;
   margin: 10% auto;
}

form > h3 {
   font-size: 17px;
   font-weight: bold;
   margin: 15px 0;
   color: orangered;
   text-transform: uppercase;
}

form > .error {
   margin: 0;
   font-size: 15px;
   font-weight: normal;
   color: orange;
   opacity: 0.7;
}

form > input, form > textarea {
   border: 3px solid orangered;
   box-shadow: unset;
   padding: 13px 12px;
   margin: 12px auto;
   width: 80%;
   font-size: 13px;
   font-weight: 500;
}

form > input:focus, form > textarea:focus {
   border: 3px solid orangered;
   box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.2);
   outline: unset;
}

form > button {
   justify-self: center;
   padding: 12px 25px;
   border-radius: 0;
   text-transform: uppercase;
   font-weight: 600;
   background: orangered;
   color: white;
   border: none;
   font-size: 14px;
   letter-spacing: -0.1px;
   cursor: pointer;
}

form > button:disabled {
   background: dimgrey;
   cursor: not-allowed;
}

Avec la feuille de style créée, vous pouvez la lier au fichier + home.html + en utilisant static tags de modèle . Ouvrez le fichier + home.html +:

nano ~/djangopush/templates/home.html

Mettez à jour la section + head + pour inclure un lien vers la feuille de style externe:

~ / djangopush / templates / home.html

{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
   ...

</head>
<body>
   ...
</body>
</html>

Assurez-vous que vous êtes dans le répertoire principal de votre projet et redémarrez votre serveur pour inspecter votre travail:

cd ~/djangopush
python manage.py runserver :8000

Lorsque vous visitez + http: //: 8000 +, il devrait ressembler à ceci:

image: https: //assets.digitalocean.com/articles/django_push_18_04/push_styled_home.png [Affichage de la page d’accueil] + Là encore, vous pouvez tuer le serveur avec + CTRL + C +.

Maintenant que vous avez créé et mis en forme la page + home.html avec succès, vous pouvez permettre aux utilisateurs de s’abonner pour envoyer des notifications chaque fois qu’ils visitent la page d’accueil.

Étape 7 - Inscription d’un agent de service et d’abonnement d’utilisateurs à des notifications push

Les notifications Web push peuvent notifier aux utilisateurs les mises à jour des applications auxquelles ils sont abonnés ou les inviter à renouer avec des applications qu’ils utilisaient auparavant. Ils s’appuient sur deux technologies, l’API push et https://developer.mozilla.org/en-US/docs/. API Web / API / Notifications_API [notifications]. Les deux technologies reposent sur la présence d’un travailleur de service.

Un push est appelé lorsque le serveur fournit des informations à l’agent de service et qu’il utilise l’API de notification pour afficher ces informations.

Nous allons abonner nos utilisateurs à l’appel puis envoyer les informations de l’abonnement au serveur pour les enregistrer.

Dans le répertoire + static +, créez un dossier nommé + js +:

mkdir ~/djangopush/static/js

Créez un fichier nommé + registerSw.js +:

nano ~/djangopush/static/js/registerSw.js

Ajoutez le code suivant, qui vérifie si les opérateurs de service sont pris en charge sur le navigateur de l’utilisateur avant de tenter d’enregistrer un agent de service:

~ / djangopush / static / js / registerSw.js

const registerSw = async () => {
   if ('serviceWorker' in navigator) {
       const reg = await navigator.serviceWorker.register('sw.js');
       initialiseState(reg)

   } else {
       showNotAllowed("You can't send push notifications ☹️😢")
   }
};

Tout d’abord, la fonction + registerSw + vérifie si le navigateur prend en charge les agents de service avant de les enregistrer. Après l’enregistrement, il appelle la fonction + initializeState + avec les données d’enregistrement. Si les opérateurs de service ne sont pas pris en charge dans le navigateur, il appelle la fonction + showNotAllowed +.

Ensuite, ajoutez le code suivant sous la fonction + registerSw + pour vérifier si un utilisateur est éligible pour recevoir des notifications push avant de tenter de les abonner:

~ / djangopush / static / js / registerSw.js

...

const initialiseState = (reg) => {
   if (!reg.showNotification) {
       showNotAllowed('Showing notifications isn\'t supported ☹️😢');
       return
   }
   if (Notification.permission === 'denied') {
       showNotAllowed('You prevented us from showing notifications ☹️🤔');
       return
   }
   if (!'PushManager' in window) {
       showNotAllowed("Push isn't allowed in your browser 🤔");
       return
   }
   subscribe(reg);
}

const showNotAllowed = (message) => {
   const button = document.querySelector('form>button');
   button.innerHTML = `${message}`;
   button.setAttribute('disabled', 'true');
};

La fonction + initializeState + vérifie les éléments suivants:

  • Indique si l’utilisateur a activé ou non les notifications, en utilisant la valeur + reg.showNotification +.

  • Indique si l’utilisateur a autorisé l’application à afficher des notifications.

  • Indique si le navigateur prend en charge l’API + PushManager +. Si l’une de ces vérifications échoue, la fonction + showNotAllowed + est appelée et l’abonnement est annulé.

La fonction + show Not Allowed affiche un message sur le bouton et le désactive si un utilisateur n’est pas éligible pour recevoir des notifications. Il affiche également les messages appropriés si un utilisateur a empêché l’application d’afficher des notifications ou si le navigateur ne prend pas en charge les notifications push.

Une fois que nous nous assurons qu’un utilisateur est éligible pour recevoir des notifications push, l’étape suivante consiste à les abonner à l’aide de + pushManager +. Ajoutez le code suivant sous la fonction + showNotAllowed +:

~ / djangopush / static / js / registerSw.js

...

function urlB64ToUint8Array(base64String) {
   const padding = '='.repeat((4 - base64String.length % 4) % 4);
   const base64 = (base64String + padding)
       .replace(/\-/g, '+')
       .replace(/_/g, '/');

   const rawData = window.atob(base64);
   const outputArray = new Uint8Array(rawData.length);
   const outputData = outputArray.map((output, index) => rawData.charCodeAt(index));

   return outputData;
}

const subscribe = async (reg) => {
   const subscription = await reg.pushManager.getSubscription();
   if (subscription) {
       sendSubData(subscription);
       return;
   }

   const vapidMeta = document.querySelector('meta[name="vapid-key"]');
   const key = vapidMeta.content;
   const options = {
       userVisibleOnly: true,
       // if key exists, create applicationServerKey property
       ...(key && {applicationServerKey: urlB64ToUint8Array(key)})
   };

   const sub = await reg.pushManager.subscribe(options);
   sendSubData(sub)
};

L’appel de la fonction + pushManager.getSubscription + renvoie les données d’un abonnement actif. Lorsqu’un abonnement actif existe, la fonction + sendSubData + est appelée avec les informations d’abonnement transmises en tant que paramètre.

En l’absence d’abonnement actif, la clé publique VAPID, codée en base64 avec une URL sécurisée, est convertie en Uint8Array à l’aide de la fonction + urlB64ToUint8Array +. + pushManager.subscribe + est ensuite appelé avec la clé publique VAPID et la valeur + userVisible + comme options. Vous pouvez en savoir plus sur les options disponibles à l’adresse here.

Une fois l’abonnement d’un utilisateur réussi, l’étape suivante consiste à envoyer les données d’abonnement au serveur. Les données seront envoyées au noeud final + webpush / save_information + fourni par le paquetage + django-webpush +. Ajoutez le code suivant sous la fonction + subscribe +:

~ / djangopush / static / js / registerSw.js

...

const sendSubData = async (subscription) => {
   const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();
   const data = {
       status_type: 'subscribe',
       subscription: subscription.toJSON(),
       browser: browser,
   };

   const res = await fetch('/webpush/save_information', {
       method: 'POST',
       body: JSON.stringify(data),
       headers: {
           'content-type': 'application/json'
       },
       credentials: "include"
   });

   handleResponse(res);
};

const handleResponse = (res) => {
   console.log(res.status);
};

registerSw();

Le point de terminaison + save_information + nécessite des informations sur l’état de la souscription (+ subscribe + et + unsubscribe +), les données de la souscription et le navigateur. Enfin, nous appelons la fonction + registerSw () + pour commencer le processus d’abonnement de l’utilisateur.

Le fichier terminé ressemble à ceci:

~ / djangopush / static / js / registerSw.js

const registerSw = async () => {
   if ('serviceWorker' in navigator) {
       const reg = await navigator.serviceWorker.register('sw.js');
       initialiseState(reg)

   } else {
       showNotAllowed("You can't send push notifications ☹️😢")
   }
};

const initialiseState = (reg) => {
   if (!reg.showNotification) {
       showNotAllowed('Showing notifications isn\'t supported ☹️😢');
       return
   }
   if (Notification.permission === 'denied') {
       showNotAllowed('You prevented us from showing notifications ☹️🤔');
       return
   }
   if (!'PushManager' in window) {
       showNotAllowed("Push isn't allowed in your browser 🤔");
       return
   }
   subscribe(reg);
}

const showNotAllowed = (message) => {
   const button = document.querySelector('form>button');
   button.innerHTML = `${message}`;
   button.setAttribute('disabled', 'true');
};

function urlB64ToUint8Array(base64String) {
   const padding = '='.repeat((4 - base64String.length % 4) % 4);
   const base64 = (base64String + padding)
       .replace(/\-/g, '+')
       .replace(/_/g, '/');

   const rawData = window.atob(base64);
   const outputArray = new Uint8Array(rawData.length);
   const outputData = outputArray.map((output, index) => rawData.charCodeAt(index));

   return outputData;
}

const subscribe = async (reg) => {
   const subscription = await reg.pushManager.getSubscription();
   if (subscription) {
       sendSubData(subscription);
       return;
   }

   const vapidMeta = document.querySelector('meta[name="vapid-key"]');
   const key = vapidMeta.content;
   const options = {
       userVisibleOnly: true,
       // if key exists, create applicationServerKey property
       ...(key && {applicationServerKey: urlB64ToUint8Array(key)})
   };

   const sub = await reg.pushManager.subscribe(options);
   sendSubData(sub)
};

const sendSubData = async (subscription) => {
   const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();
   const data = {
       status_type: 'subscribe',
       subscription: subscription.toJSON(),
       browser: browser,
   };

   const res = await fetch('/webpush/save_information', {
       method: 'POST',
       body: JSON.stringify(data),
       headers: {
           'content-type': 'application/json'
       },
       credentials: "include"
   });

   handleResponse(res);
};

const handleResponse = (res) => {
   console.log(res.status);
};

registerSw();

Ensuite, ajoutez une balise + script pour le fichier` + register.js` dans + home.html +. Ouvrez le fichier:

nano ~/djangopush/templates/home.html

Ajoutez la balise + script avant la balise de fermeture de l’élément` + body`:

~ / djangopush / templates / home.html

{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
  ...
</head>
<body>
  ...

</body>
</html>

Dans la mesure où un agent de service n’existe pas encore, si vous laissiez votre application en cours d’exécution ou tentiez de la redémarrer, un message d’erreur apparaît. Corrigeons cela en créant un service worker.

Étape 8 - Création d’un travailleur de service

Pour afficher une notification push, vous devez installer un agent de service actif sur la page d’accueil de votre application. Nous allons créer un agent de service qui écoute les événements + push + et affiche les messages quand il est prêt.

Parce que nous voulons que le service worker couvre l’ensemble du domaine, nous devons l’installer à la racine de l’application. Vous pouvez en savoir plus sur le processus dans cet article en décrivant mettre pour enregistrer un prestataire de services. Notre approche consistera à créer un fichier + sw.js dans le dossier` + templates`, que nous enregistrerons ensuite en tant que vue.

Créez le fichier:

nano ~/djangopush/templates/sw.js

Ajoutez le code suivant, qui indique au technicien de service d’écouter les événements Push:

~ / djangopush / templates / sw.js

// Register event listener for the 'push' event.
self.addEventListener('push', function (event) {
   // Retrieve the textual payload from event.data (a PushMessageData object).
   // Other formats are supported (ArrayBuffer, Blob, JSON), check out the documentation
   // on https://developer.mozilla.org/en-US/docs/Web/API/PushMessageData.
   const eventInfo = event.data.text();
   const data = JSON.parse(eventInfo);
   const head = data.head || 'New Notification 🕺🕺';
   const body = data.body || 'This is default content. Your notification didn\'t have one 🙄🙄';

   // Keep the service worker alive until the notification is created.
   event.waitUntil(
       self.registration.showNotification(head, {
           body: body,
           icon: 'https://i.imgur.com/MZM3K5w.png'
       })
   );
});

Le technicien de service écoute un événement push. Dans la fonction de rappel, les données + event + sont converties en texte. Nous utilisons les chaînes par défaut + title + et + body + si les données d’événement ne les ont pas. La fonction + showNotification + prend le titre de la notification, l’en-tête de la notification à afficher et une options objet en tant que paramètres. L’objet options contient plusieurs propriétés permettant de configurer les options visuelles d’une notification.

Pour que votre technicien puisse travailler sur l’intégralité de votre domaine, vous devez l’installer à la racine de l’application. Nous utiliserons https://docs.djangoproject.com/fr/2.1/topics/class-based-views/ [+ TemplateView +] pour autoriser l’agent du service à accéder à l’ensemble du domaine.

Ouvrez le fichier + urls.py +:

nano ~/djangopush/djangopush/urls.py

Ajoutez une nouvelle instruction d’importation et un chemin dans la liste + urlpatterns + pour créer une vue basée sur les classes:

~ / djangopush / djangopush / urls.py

...


urlpatterns = [
                 ...

             ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

Les vues basées sur les classes telles que + TemplateView + vous permettent de créer des vues flexibles et réutilisables. Dans ce cas, la méthode + TemplateView.as_view + crée un chemin pour l’agent de service en transmettant l’agent de service créé récemment en tant que modèle et + application / x-javascript + en tant que + contenu_type + du modèle.

Vous avez maintenant créé un technicien de service et l’avez enregistré en tant que route. Ensuite, vous allez configurer le formulaire sur la page d’accueil pour envoyer des notifications push.

Étape 9 - Envoi de notifications push

À l’aide du formulaire de la page d’accueil, les utilisateurs devraient pouvoir envoyer des notifications push pendant que votre serveur est en cours d’exécution. Vous pouvez également envoyer des notifications push à l’aide de n’importe quel service RESTful tel que Postman. Lorsque l’utilisateur envoie des notifications push à partir du formulaire situé sur la page d’accueil, les données incluent un + tête + et + corps +, ainsi que le + id + de l’utilisateur destinataire. Les données doivent être structurées de la manière suivante:

{
   head: "Title of the notification",
   body: "Notification body",
   id: "User's id"
}

Pour écouter l’événement + submit + du formulaire et envoyer les données saisies par l’utilisateur au serveur, nous allons créer un fichier nommé + site.js + dans le répertoire + ~ / djangopush / static / js +.

Ouvrez le fichier:

nano ~/djangopush/static/js/site.js

Tout d’abord, ajoutez un écouteur d’événement + submit + au formulaire qui vous permettra d’obtenir les valeurs des entrées de formulaire et l’ID utilisateur stocké dans la balise + meta + de votre modèle:

~ / djangopush / static / js / site.js

const pushForm = document.getElementById('send-push__form');
const errorMsg = document.querySelector('.error');

pushForm.addEventListener('submit', async function (e) {
   e.preventDefault();
   const input = this[0];
   const textarea = this[1];
   const button = this[2];
   errorMsg.innerText = '';

   const head = input.value;
   const body = textarea.value;
   const meta = document.querySelector('meta[name="user_id"]');
   const id = meta ? meta.content : null;
   ...
   // TODO: make an AJAX request to send notification
});

La fonction + pushForm + obtient les` + input`, + textarea et` + button` à l’intérieur du formulaire. Il obtient également les informations de la balise + meta +, y compris l’attribut name + user_id + et l’id de l’utilisateur stocké dans l’attribut + content + de la balise. Avec ces informations, il peut envoyer une demande POST au noeud final + / send_push + sur le serveur.

Pour envoyer des demandes au serveur, nous utiliserons l’API native Fetch. Nous utilisons Fetch ici, car il est pris en charge par la plupart des navigateurs et n’a pas besoin de bibliothèques externes pour fonctionner. Sous le code que vous avez ajouté, mettez à jour la fonction + pushForm + pour inclure le code d’envoi de requêtes AJAX:

~ / djangopush / static / js / site.js

const pushForm = document.getElementById('send-push__form');
const errorMsg = document.querySelector('.error');

pushForm.addEventListener('submit', async function (e) {
    ...
   const id = meta ? meta.content : null;

    if (head && body && id) {
       button.innerText = 'Sending...';
       button.disabled = true;

       const res = await fetch('/send_push', {
           method: 'POST',
           body: JSON.stringify({head, body, id}),
           headers: {
               'content-type': 'application/json'
           }
       });
       if (res.status === 200) {
           button.innerText = 'Send another 😃!';
           button.disabled = false;
           input.value = '';
           textarea.value = '';
       } else {
           errorMsg.innerText = res.message;
           button.innerText = 'Something broke 😢..  Try again?';
           button.disabled = false;
       }
   }
   else {
       let error;
       if (!head || !body){
           error = 'Please ensure you complete the form 🙏🏾'
       }
       else if (!id){
           error = "Are you sure you're logged in? 🤔. Make sure! 👍🏼"
       }
       errorMsg.innerText = error;
   }
});

Si les trois paramètres requis + head +, '+ body + et + + id + sont présents, nous envoyons la demande et désactivons temporairement le bouton d’envoi.

Le fichier terminé ressemble à ceci:

~ / djangopush / static / js / site.js

const pushForm = document.getElementById('send-push__form');
const errorMsg = document.querySelector('.error');

pushForm.addEventListener('submit', async function (e) {
   e.preventDefault();
   const input = this[0];
   const textarea = this[1];
   const button = this[2];
   errorMsg.innerText = '';

   const head = input.value;
   const body = textarea.value;
   const meta = document.querySelector('meta[name="user_id"]');
   const id = meta ? meta.content : null;

   if (head && body && id) {
       button.innerText = 'Sending...';
       button.disabled = true;

       const res = await fetch('/send_push', {
           method: 'POST',
           body: JSON.stringify({head, body, id}),
           headers: {
               'content-type': 'application/json'
           }
       });
       if (res.status === 200) {
           button.innerText = 'Send another 😃!';
           button.disabled = false;
           input.value = '';
           textarea.value = '';
       } else {
           errorMsg.innerText = res.message;
           button.innerText = 'Something broke 😢..  Try again?';
           button.disabled = false;
       }
   }
   else {
       let error;
       if (!head || !body){
           error = 'Please ensure you complete the form 🙏🏾'
       }
       else if (!id){
           error = "Are you sure you're logged in? 🤔. Make sure! 👍🏼"
       }
       errorMsg.innerText = error;
   }
});

Enfin, ajoutez le fichier + site.js à` + home.html`:

nano ~/djangopush/templates/home.html

Ajoutez la balise + script:

~ / djangopush / templates / home.html

{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
  ...
</head>
<body>
  ...

</body>
</html>

À ce stade, si vous laissiez votre application en cours d’exécution ou tentiez de la redémarrer, une erreur se produirait, car les agents de service ne peuvent fonctionner que dans des domaines sécurisés ou sur + localhost +. Dans l’étape suivante, nous utiliserons ngrok pour créer un tunnel sécurisé vers notre serveur Web.

Étape 10 - Création d’un tunnel sécurisé pour tester l’application

Les agents de service ont besoin de connexions sécurisées pour fonctionner sur n’importe quel site, à l’exception de + localhost + car ils peuvent autoriser le détournement de connexions et le filtrage et la fabrication de réponses. Pour cette raison, nous allons créer un tunnel sécurisé pour notre serveur avec https://ngrok.com [ngrok].

Ouvrez une deuxième fenêtre de terminal et assurez-vous que vous êtes dans votre répertoire personnel:

cd ~

Si vous avez commencé avec un serveur 18.04 vierge dans les conditions préalables, vous devrez installer + unzip +:

sudo apt update && sudo apt install unzip

Télécharger ngrok:

wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
unzip ngrok-stable-linux-amd64.zip

Déplacez + ngrok + vers + / usr / local / bin + pour avoir accès à la commande + ngrok + depuis le terminal:

sudo mv ngrok /usr/local/bin

Dans votre première fenêtre de terminal, assurez-vous que vous êtes dans le répertoire de votre projet et démarrez votre serveur:

cd ~/djangopush
python manage.py runserver :8000

Vous devrez le faire avant de créer un tunnel sécurisé pour votre application.

Dans votre deuxième fenêtre de terminal, accédez au dossier de votre projet et activez votre environnement virtuel:

cd ~/djangopush
source my_env/bin/activate

Créez le tunnel sécurisé pour votre application:

ngrok http :8000

Vous verrez la sortie suivante, qui comprend des informations sur votre URL sécurisée ngrok:

Outputngrok by @inconshreveable                                                                                                                       (Ctrl+C to quit)

Session Status                online
Session Expires               7 hours, 59 minutes
Version                       2.2.8
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http:// -> 203.0.113.0:8000
Forwarding                    https:// -> 203.0.113.0:8000

Connections                   ttl     opn     rt1     rt5     p50     p90
                             0       0       0.00    0.00    0.00    0.00

Copiez le ++ à partir de la sortie de la console. Vous devrez l’ajouter à la liste de + ALLOWED_HOSTS dans votre fichier` + settings.py`.

Ouvrez une autre fenêtre de terminal, accédez au dossier de votre projet et activez votre environnement virtuel:

cd ~/djangopush
source my_env/bin/activate

Ouvrez le fichier + settings.py +:

nano ~/djangopush/djangopush/settings.py

Mettez à jour la liste de + ALLOWED_HOSTS + avec le tunnel sécurisé ngrok:

~ / djangopush / djangopush / settings.py

...

ALLOWED_HOSTS = ['your_server_ip', '']
...

Accédez à la page d’administration sécurisée pour vous connecter: + https: /// admin / +. Vous verrez un écran qui ressemble à ceci:

image: https: //assets.digitalocean.com/articles/django_push_18_04/ngrok_login.png [connexion administrateur ngrok]

Entrez vos informations d’utilisateur administrateur Django sur cet écran. Il devrait s’agir des mêmes informations que vous avez saisies lorsque vous vous êtes connecté à l’interface d’administration dans la page https://www.digitalocean.com/community/tutorials/how-to-install-the-django-web-framework-on-ubuntu-18. -04 # testing-the-development-server [étapes préalables]. Vous êtes maintenant prêt à envoyer des notifications push.

Visitez + https: // + dans votre navigateur. Vous verrez une invite vous demandant la permission d’afficher des notifications. Cliquez sur le bouton * Autoriser * pour permettre à votre navigateur d’afficher des notifications push:

image: https: //assets.digitalocean.com/articles/django_push_18_04/allow_push_two.png [demande de notifications push]

Soumettre un formulaire rempli affichera une notification semblable à ceci:

image: https: //assets.digitalocean.com/articles/django_push_18_04/web_push_final.png [capture d’écran de la notification]

Si vous avez reçu des notifications, votre application fonctionne comme prévu.

Vous avez créé une application Web qui déclenche des notifications push sur le serveur et, avec l’aide des opérateurs de service, reçoit et affiche des notifications. Vous avez également suivi la procédure d’obtention des clés VAPID requises pour envoyer des notifications push à partir d’un serveur d’applications.

Conclusion

Dans ce didacticiel, vous avez appris à abonner des utilisateurs pour envoyer des notifications en mode push, installer des techniciens de maintenance et afficher des notifications en utilisant l’API de notification.

Vous pouvez aller encore plus loin en configurant les notifications pour ouvrir des zones spécifiques de votre application lorsque vous cliquez dessus. Le code source de ce didacticiel est disponible à l’adresse here.