DjangoアプリケーションからWebプッシュ通知を送信する方法
序章
Webは絶えず進化しており、以前はネイティブモバイルデバイスでしか利用できなかった機能を実現できるようになりました。 JavaScript サービスワーカーの導入により、バックグラウンド同期、オフラインキャッシュ、プッシュ通知の送信などを行うための新しい機能がWebに提供されました。
プッシュ通知を使用すると、ユーザーはオプトインしてモバイルおよびWebアプリケーションの更新を受信できます。 また、ユーザーは、カスタマイズされた関連コンテンツを使用して、既存のアプリケーションを再利用できます。
このチュートリアルでは、Ubuntu 18.04でDjangoアプリケーションをセットアップします。このアプリケーションは、ユーザーがアプリケーションにアクセスする必要があるアクティビティがあるたびにプッシュ通知を送信します。 これらの通知を作成するには、 Django-Webpush パッケージを使用し、サービスワーカーを設定して登録し、クライアントに通知を表示します。 通知付きの動作中のアプリケーションは次のようになります。
前提条件
このガイドを開始する前に、次のものが必要です。
- 非rootユーザーとアクティブなファイアウォールを備えた1つのUbuntu18.04サーバー。 Ubuntu 18.04サーバーの作成方法の詳細については、この初期サーバーセットアップガイドのガイドラインに従うことができます。
pip
とvenv
これらのガイドラインに従ってインストールされます。- と呼ばれるDjangoプロジェクト
djangopush
ホームディレクトリに作成し、 Ubuntu18.04でサンプルDjangoプロジェクトを作成する際のガイドラインに従って設定します。 必ずサーバーのIPアドレスをALLOWED_HOSTSディレクティブに追加してください。settings.py
ファイル。
ステップ1—Django-WebpushのインストールとVapidキーの取得
Django-Webpushは、開発者がWebプッシュ通知をDjangoアプリケーションに統合して送信できるようにするパッケージです。 このパッケージを使用して、アプリケーションからプッシュ通知をトリガーして送信します。 このステップでは、Django-Webpushをインストールし、サーバーを識別して各リクエストの一意性を確保するために必要な任意のアプリケーションサーバー識別(VAPID)キーを取得します。
あなたがにいることを確認してください ~/djangopush
前提条件で作成したプロジェクトディレクトリ:
- cd ~/djangopush
仮想環境をアクティブ化します。
- source my_env/bin/activate
のバージョンをアップグレードする pip
最新であることを確認するには:
- pip install --upgrade pip
Django-Webpushをインストールします。
- pip install django-webpush
パッケージをインストールした後、それをアプリケーションのリストに追加します settings.py
ファイル。 最初に開く settings.py
:
- nano ~/djangopush/djangopush/settings.py
追加 webpush
のリストに INSTALLED_APPS
:
...
INSTALLED_APPS = [
...,
'webpush',
]
...
ファイルを保存して、エディターを終了します。
アプリケーションでmigrationsを実行して、データベーススキーマに加えた変更を適用します。
- python manage.py migrate
出力は次のようになり、移行が成功したことを示します。
OutputOperations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, webpush
Running migrations:
Applying webpush.0001_initial... OK
Webプッシュ通知を設定する次のステップは、VAPIDキーを取得することです。 これらのキーはアプリケーションサーバーを識別し、サブスクリプションを特定のサーバーに制限するため、プッシュサブスクリプションURLの機密性を減らすために使用できます。
VAPIDキーを取得するには、 wep-push-codelabWebアプリケーションに移動します。 ここでは、自動的に生成されたキーが提供されます。 秘密鍵と公開鍵をコピーします。
次に、で新しいエントリを作成します settings.py
VAPID情報について。 まず、ファイルを開きます。
- nano ~/djangopush/djangopush/settings.py
次に、という新しいディレクティブを追加します WEBPUSH_SETTINGS
VAPIDの公開鍵と秘密鍵、および以下のメールアドレスを使用して AUTH_PASSWORD_VALIDATORS
:
...
AUTH_PASSWORD_VALIDATORS = [
...
]
WEBPUSH_SETTINGS = {
"VAPID_PUBLIC_KEY": "your_vapid_public_key",
"VAPID_PRIVATE_KEY": "your_vapid_private_key",
"VAPID_ADMIN_EMAIL": "[email protected]"
}
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
...
プレースホルダーの値を置き換えることを忘れないでください your_vapid_public_key
, your_vapid_private_key
、 と [email protected]
あなた自身の情報で。 あなたのメールアドレスは、プッシュサーバーで問題が発生した場合に通知される方法です。
次に、アプリケーションのホームページを表示し、サブスクライブしたユーザーへのプッシュ通知をトリガーするビューを設定します。
ステップ2—ビューを設定する
このステップでは、基本的なセットアップを行います home
viewとHttpResponse応答オブジェクトのホームページ、および send_push
見る。 ビューは、Webリクエストから応答オブジェクトを返す関数です。 The send_push
ビューはDjango-Webpushライブラリを使用して、ユーザーがホームページに入力したデータを含むプッシュ通知を送信します。
に移動します ~/djangopush/djangopush
フォルダ:
- cd ~/djangopush/djangopush
ランニング ls
フォルダ内には、プロジェクトのメインファイルが表示されます。
Output/__init__.py
/settings.py
/urls.py
/wsgi.py
このフォルダ内のファイルは、によって自動生成されます django-admin
前提条件でプロジェクトを作成するために使用したユーティリティ。 The settings.py
ファイルには、インストールされているアプリケーションや静的ルートフォルダーなどのプロジェクト全体の構成が含まれています。 The urls.py
ファイルには、プロジェクトのURL構成が含まれています。 ここで、作成したビューに一致するルートを設定します。
内に新しいファイルを作成します ~/djangopush/djangopush
と呼ばれるディレクトリ views.py
、プロジェクトのビューが含まれます。
- nano ~/djangopush/djangopush/views.py
最初に作成するビューは home
ビュー。ユーザーがプッシュ通知を送信できるホームページが表示されます。 次のコードをファイルに追加します。
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>')
The home
ビューはによって装飾されています require_GET
デコレータ。ビューをGETリクエストのみに制限します。 ビューは通常、ビューに対して行われたすべての要求に対する応答を返します。 このビューは、応答として単純なHTMLタグを返します。
次に作成するビューは send_push
、を使用して送信されたプッシュ通知を処理します django-webpush
パッケージ。 POSTリクエストのみに制限され、クロスサイトリクエストフォージェリ(CSRF)保護が免除されます。 これを行うと、Postmanまたはその他のRESTfulサービスを使用してビューをテストできます。 ただし、本番環境では、ビューがCSRFに対して脆弱なままにならないように、このデコレータを削除する必要があります。
を作成するには send_push
ビューで、最初に次のインポートを追加してJSON応答を有効にし、 send_user_notification
の機能 webpush
図書館:
from django.http.response import JsonResponse, HttpResponse
from django.views.decorators.http import require_GET, require_POST
from django.shortcuts import get_object_or_404
from django.contrib.auth.models import User
from django.views.decorators.csrf import csrf_exempt
from webpush import send_user_notification
import json
次に、 require_POST
デコレータ。ユーザーから送信されたリクエスト本文を使用して、プッシュ通知を作成およびトリガーします。
@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"})
に2つのデコレータを使用しています send_push
ビュー: require_POST
ビューをPOSTリクエストのみに制限するデコレータ、および csrf_exempt
CSRF保護からビューを免除するデコレータ。
このビューはPOSTデータを想定しており、次のことを行います。 body
リクエストの処理を行い、 json パッケージを使用して、json.loadsを使用してJSONドキュメントをPythonオブジェクトに逆シリアル化します。 json.loads
構造化されたJSONドキュメントを取得し、Pythonオブジェクトに変換します。
ビューは、リクエスト本文オブジェクトに次の3つのプロパティがあることを想定しています。
head
:プッシュ通知のタイトル。body
:通知の本文。id
:id
リクエストユーザーの。
必要なプロパティのいずれかが欠落している場合、ビューは JSONResponse
404「見つかりません」ステータス。 指定された主キーを持つユーザーが存在する場合、ビューは user
get_object_or_404関数を使用して一致する主キーを使用します。 django.shortcuts
図書館。 ユーザーが存在しない場合、関数は404エラーを返します。
ビューも利用します send_user_notification
からの機能 webpush
図書館。 この関数は3つのパラメーターを取ります。
User
:プッシュ通知の受信者。payload
:通知を含む通知情報head
とbody
.ttl
:ユーザーがオフラインの場合に通知を保存する最大時間(秒単位)。
エラーが発生しない場合、ビューは JSONResponse
200の「成功」ステータスとデータオブジェクトがあります。 もし KeyError
発生すると、ビューは500の「内部サーバーエラー」ステータスを返します。 A KeyError
オブジェクトの要求されたキーが存在しない場合に発生します。
次のステップでは、作成したビューに一致する対応するURLルートを作成します。
ステップ3—URLをビューにマッピングする
Djangoを使用すると、URLを作成してビューに接続することができます。 URLconf
. このモジュールは、URLパス式をPython関数(ビュー)にマップします。 通常、URL構成ファイルは、プロジェクトの作成時に自動生成されます。 このステップでは、このファイルを更新して、前のステップで作成したビューの新しいルートと、 django-webpush
アプリ。プッシュ通知をユーザーにサブスクライブするためのエンドポイントを提供します。
ビューの詳細については、Djangoビューの作成方法を参照してください。
開ける urls.py
:
- nano ~/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),
]
次のステップは、作成したビューをURLにマップすることです。 まず、を追加します include
インポートして、Django-Webpushライブラリのすべてのルートがプロジェクトに追加されるようにします。
"""webpushdjango URL Configuration
...
"""
from django.contrib import admin
from django.urls import path, include
次に、最後の手順で作成したビューをインポートして、 urlpatterns
ビューをマップするリスト:
"""webpushdjango URL Configuration
...
"""
from django.contrib import admin
from django.urls import path, include
from .views import home, send_push
urlpatterns = [
path('admin/', admin.site.urls),
path('', home),
path('send_push', send_push),
path('webpush/', include('webpush.urls')),
]
ここでは、 urlpatterns
リストはのURLを登録します django-webpush
ビューをパッケージ化してURLにマッピングします /send_push
と /home
.
テストしてみましょう /home
意図したとおりに機能していることを確認してください。 プロジェクトのルートディレクトリにいることを確認してください。
- cd ~/djangopush
次のコマンドを実行してサーバーを起動します。
- python manage.py runserver your_server_ip:8000
案内する http://your_server_ip:8000
. 次のホームページが表示されます。
この時点で、サーバーを強制終了できます CTRL+C
、そしてテンプレートの作成に移り、を使用してビューにレンダリングします。 render
関数。
ステップ4—テンプレートの作成
Djangoのテンプレートエンジンを使用すると、HTMLファイルに似たテンプレートを使用してアプリケーションのユーザー向けレイヤーを定義できます。 このステップでは、テンプレートを作成してレンダリングします。 home
見る。
というフォルダを作成します templates
プロジェクトのルートディレクトリ:
- mkdir ~/djangopush/templates
実行した場合 ls
この時点でのプロジェクトのルートフォルダーでは、出力は次のようになります。
Output/djangopush
/templates
db.sqlite3
manage.py
/my_env
というファイルを作成します home.html
の中に templates
フォルダ:
- nano ~/djangopush/templates/home.html
次のコードをファイルに追加して、ユーザーが情報を入力してプッシュ通知を作成できるフォームを作成します。
{% 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>
The body
ファイルのには、次の2つのフィールドを持つフォームが含まれています。 input
要素は通知の見出し/タイトルと textarea
要素は通知本文を保持します。
の中に head
ファイルのセクションには、2つあります meta
VAPID公開鍵とユーザーのIDを保持するタグ。 これらの2つの変数は、ユーザーを登録してプッシュ通知を送信するために必要です。 サーバーにAJAXリクエストを送信するため、ここではユーザーのIDが必要です。 id
ユーザーを識別するために使用されます。 現在のユーザーが登録ユーザーの場合、テンプレートは meta
彼らのタグ id
コンテンツとして。
次のステップは、テンプレートの場所をDjangoに指示することです。 これを行うには、編集します settings.py
を更新します TEMPLATES
リスト。
を開きます settings.py
ファイル:
- nano ~/djangopush/djangopush/settings.py
以下をに追加します DIRS
テンプレートディレクトリへのパスを指定するリスト:
...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
...
],
},
},
]
...
次に、あなたの views.py
ファイル、更新 home
レンダリングするビュー home.html
テンプレート。 ファイルを開きます。
- nano ~/djangpush/djangopush/views.py
まず、以下を含むいくつかの追加のインポートを追加します settings
構成。これには、プロジェクトのすべての設定が含まれます。 settings.py
ファイル、および render
からの機能 django.shortcuts
:
...
from django.shortcuts import render, get_object_or_404
...
import json
from django.conf import settings
...
次に、追加した初期コードを削除します home
以下を表示して追加します。これは、作成したテンプレートのレンダリング方法を指定します。
...
@require_GET
def home(request):
webpush_settings = getattr(settings, 'WEBPUSH_SETTINGS', {})
vapid_key = webpush_settings.get('VAPID_PUBLIC_KEY')
user = request.user
return render(request, 'home.html', {user: user, 'vapid_key': vapid_key})
このコードは、次の変数を割り当てます。
webpush_settings
:これには、の値が割り当てられますWEBPUSH_SETTINGS
からの属性settings
構成。vapid_key
:これはVAPID_PUBLIC_KEY
からの値webpush_settings
クライアントに送信するオブジェクト。 この公開鍵は秘密鍵と照合され、公開鍵を持つクライアントがサーバーからのプッシュメッセージの受信を許可されていることを確認します。user
:この変数は、着信要求から取得されます。 ユーザーがサーバーにリクエストを送信するたびに、そのユーザーの詳細がuser
分野。
レンダリング関数は、HTMLファイルと、現在のユーザーとサーバーのvapid公開鍵を含むコンテキストオブジェクトを返します。 ここでは3つのパラメータが必要です。 request
、 template
レンダリングされるオブジェクト、およびテンプレートで使用される変数を含むオブジェクト。
テンプレートを作成し、 home
ビューが更新されたら、静的ファイルを提供するようにDjangoを構成することに進むことができます。
ステップ5—静的ファイルの提供
Webアプリケーションには、CSS、JavaScript、およびDjangoが「静的ファイル」と呼ぶその他の画像ファイルが含まれます。 Djangoを使用すると、プロジェクト内の各アプリケーションからすべての静的ファイルを、それらが提供される単一の場所に収集できます。 このソリューションはと呼ばれます django.contrib.staticfiles
. このステップでは、設定を更新して、静的ファイルが保存される場所をDjangoに通知します。
開ける settings.py
:
- nano ~/djangopush/djangopush/settings.py
の settings.py
、最初に次のことを確認します STATIC_URL
定義されています:
...
STATIC_URL = '/static/'
次に、というディレクトリのリストを追加します STATICFILES_DIRS
Djangoが静的ファイルを探す場所:
...
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
これで、 STATIC_URL
で定義されたパスのリストに urls.py
ファイル。
ファイルを開きます。
- nano ~/djangopush/djangopush/urls.py
次のコードを追加します。これにより、 static
URL構成を更新し、 urlpatterns
リスト。 ここでのヘルパー関数は、 STATIC_URL
と STATIC_ROOT
で提供したプロパティ settings.py
プロジェクトの静的ファイルを提供するファイル:
...
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
...
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
静的ファイル設定を構成したら、アプリケーションのホームページのスタイル設定に進むことができます。
ステップ6—ホームページのスタイリング
静的ファイルを提供するようにアプリケーションを設定した後、外部スタイルシートを作成して、 home.html
ホームページのスタイルを設定するファイル。 すべての静的ファイルはに保存されます static
プロジェクトのルートフォルダにあるディレクトリ。
作成する static
フォルダと css
内のフォルダ static
フォルダ:
- mkdir -p ~/djangopush/static/css
と呼ばれるcssファイルを開きます styles.css
中 css
フォルダ:
- nano ~/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;
}
作成されたスタイルシートを使用して、スタイルシートをにリンクできます。 home.html
静的テンプレートタグを使用したファイル。 を開きます home.html
ファイル:
- nano ~/djangopush/templates/home.html
を更新します head
外部スタイルシートへのリンクを含めるセクション:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
...
<link href="{% static '/css/styles.css' %}" rel="stylesheet">
</head>
<body>
...
</body>
</html>
メインプロジェクトディレクトリにいることを確認し、サーバーを再起動して作業を検査します。
- cd ~/djangopush
- python manage.py runserver your_server_ip:8000
あなたが訪問するとき http://your_server_ip:8000
、次のようになります。
繰り返しますが、サーバーを強制終了できます CTRL+C
.
これで、 home.html
ページとスタイルを設定すると、ユーザーがホームページにアクセスするたびに通知をプッシュするようにサブスクライブできます。
ステップ7—サービスワーカーの登録とプッシュ通知へのユーザーのサブスクライブ
Webプッシュ通知は、サブスクライブしているアプリケーションに更新がある場合にユーザーに通知したり、過去に使用したアプリケーションに再度アクセスするようにユーザーに促したりできます。 これらは、 pushAPIとnotificationsAPIの2つのテクノロジーに依存しています。 どちらのテクノロジーも、サービスワーカーの存在に依存しています。
サーバーがServiceWorkerに情報を提供し、ServiceWorkerが通知APIを使用してこの情報を表示するとプッシュが呼び出されます。
ユーザーをプッシュにサブスクライブしてから、サブスクリプションからサーバーに情報を送信してユーザーを登録します。
の中に static
ディレクトリ、というフォルダを作成します js
:
- mkdir ~/djangopush/static/js
というファイルを作成します registerSw.js
:
- nano ~/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 ☹️😢")
}
};
まず、 registerSw
関数は、ブラウザがサービスワーカーをサポートしているかどうかを、登録する前にチェックします。 登録後、 initializeState
登録データで機能します。 ブラウザでサービスワーカーがサポートされていない場合は、 showNotAllowed
関数。
次に、以下のコードを追加します registerSw
ユーザーがプッシュ通知をサブスクライブする前に、プッシュ通知を受信する資格があるかどうかを確認する機能:
...
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');
};
The initializeState
関数は以下をチェックします:
- ユーザーが通知を有効にしているかどうか、の値を使用して
reg.showNotification
. - ユーザーが通知を表示するためのアプリケーション権限を付与したかどうか。
- ブラウザがサポートしているかどうか
PushManager
API。 これらのチェックのいずれかが失敗した場合、showNotAllowed
関数が呼び出され、サブスクリプションが中止されます。
The showNotAllowed
関数はボタンにメッセージを表示し、ユーザーが通知を受信できない場合はそれを無効にします。 また、ユーザーがアプリケーションによる通知の表示を制限している場合、またはブラウザーがプッシュ通知をサポートしていない場合にも、適切なメッセージを表示します。
ユーザーがプッシュ通知を受信する資格があることを確認したら、次のステップは、を使用してそれらをサブスクライブすることです pushManager
. 以下のコードを追加します showNotAllowed
関数:
...
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)
};
を呼び出す pushManager.getSubscription
関数は、アクティブなサブスクリプションのデータを返します。 アクティブなサブスクリプションが存在する場合、 sendSubData
関数は、パラメーターとして渡されたサブスクリプション情報を使用して呼び出されます。
アクティブなサブスクリプションが存在しない場合、Base64 URLセーフでエンコードされたVAPID公開鍵は、 urlB64ToUint8Array
関数。 pushManager.subscribe
次に、VAPID公開鍵と userVisible
オプションとしての値。 利用可能なオプションの詳細については、こちらをご覧ください。
ユーザーを正常にサブスクライブした後、次のステップはサブスクリプションデータをサーバーに送信することです。 データはに送信されます webpush/save_information
によって提供されるエンドポイント django-webpush
パッケージ。 以下のコードを追加します subscribe
関数:
...
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();
The save_information
エンドポイントには、サブスクリプションのステータスに関する情報が必要です(subscribe
と unsubscribe
)、サブスクリプションデータ、およびブラウザ。 最後に、 registerSw()
ユーザーをサブスクライブするプロセスを開始する関数。
完成したファイルは次のようになります。
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();
次に、 script
のタグ registerSw.js
のファイル home.html
. ファイルを開きます。
- nano ~/djangopush/templates/home.html
追加します script
の終了タグの前のタグ body
エレメント:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
...
<script src="{% static '/js/registerSw.js' %}"></script>
</body>
</html>
Service Workerはまだ存在しないため、アプリケーションを実行したままにするか、アプリケーションを再起動しようとすると、エラーメッセージが表示されます。 サービスワーカーを作成してこれを修正しましょう。
ステップ8—サービスワーカーの作成
プッシュ通知を表示するには、アプリケーションのホームページにアクティブなServiceWorkerがインストールされている必要があります。 リッスンするサービスワーカーを作成します push
イベントを実行し、準備ができたらメッセージを表示します。
Service Workerのスコープをドメイン全体にする必要があるため、アプリケーションのルートにインストールする必要があります。 このプロセスの詳細については、サービスワーカーの登録方法の概要を説明しています。 私たちのアプローチは、 sw.js
のファイル templates
フォルダ。ビューとして登録します。
ファイルを作成します。
- nano ~/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'
})
);
});
サービスワーカーはプッシュイベントをリッスンします。 コールバック関数では、 event
データはテキストに変換されます。 デフォルトを使用します title
と body
イベントデータに文字列がない場合は文字列。 The showNotification
関数は、通知のタイトル、表示される通知のヘッダー、およびoptionsオブジェクトをパラメーターとして受け取ります。 optionsオブジェクトには、通知の視覚的オプションを構成するためのいくつかのプロパティが含まれています。
Service Workerがドメイン全体で機能するには、アプリケーションのルートにサービスワーカーをインストールする必要があります。 TemplateView を使用して、ServiceWorkerがドメイン全体にアクセスできるようにします。
を開きます urls.py
ファイル:
- nano ~/djangopush/djangopush/urls.py
新しいインポートステートメントとパスをに追加します urlpatterns
クラスベースのビューを作成するためのリスト:
...
from django.views.generic import TemplateView
urlpatterns = [
...,
path('sw.js', TemplateView.as_view(template_name='sw.js', content_type='application/x-javascript'))
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
次のようなクラスベースのビュー TemplateView
柔軟で再利用可能なビューを作成できます。 この場合、 TemplateView.as_view
メソッドは、最近作成されたService Workerをテンプレートとして渡すことにより、ServiceWorkerのパスを作成します。 application/x-javascript
として content_type
テンプレートの。
これで、サービスワーカーが作成され、ルートとして登録されました。 次に、プッシュ通知を送信するようにホームページにフォームを設定します。
ステップ9—プッシュ通知の送信
ホームページのフォームを使用して、ユーザーはサーバーの実行中にプッシュ通知を送信できる必要があります。 PostmanなどのRESTfulサービスを使用してプッシュ通知を送信することもできます。 ユーザーがホームページのフォームからプッシュ通知を送信すると、データには head
と body
、および id
受信ユーザーの。 データは次のように構成する必要があります。
{
head: "Title of the notification",
body: "Notification body",
id: "User's id"
}
聞くために submit
フォームのイベントとユーザーが入力したデータをサーバーに送信し、というファイルを作成します site.js
の中に ~/djangopush/static/js
ディレクトリ。
ファイルを開きます。
- nano ~/djangopush/static/js/site.js
まず、を追加します submit
フォーム入力の値とに保存されているユーザーIDを取得できるようにするフォームのイベントリスナー meta
テンプレートのタグ:
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
});
The pushForm
関数は input
, textarea
、 と button
フォーム内。 また、から情報を取得します meta
name属性を含むタグ user_id
およびに保存されているユーザーのID content
タグの属性。 この情報を使用して、POSTリクエストをに送信できます。 /send_push
サーバー上のエンドポイント。
サーバーにリクエストを送信するには、ネイティブの FetchAPIを使用します。 ここではFetchを使用しています。これは、ほとんどのブラウザーでサポートされており、機能するために外部ライブラリを必要としないためです。 追加したコードの下で、 pushForm
AJAXリクエストを送信するためのコードを含める関数:
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;
}
});
3つの必要なパラメータの場合 head
, body
、 と id
が存在する場合、リクエストを送信し、送信ボタンを一時的に無効にします。
完成したファイルは次のようになります。
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;
}
});
最後に、 site.js
にファイルする home.html
:
- nano ~/djangopush/templates/home.html
追加します script
鬼ごっこ:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
...
<script src="{% static '/js/site.js' %}"></script>
</body>
</html>
この時点で、アプリケーションを実行したままにするか、アプリケーションを再起動しようとすると、エラーが表示されます。これは、ServiceWorkerが安全なドメインまたはでのみ機能できるためです。 localhost
. 次のステップでは、 ngrok を使用して、Webサーバーへの安全なトンネルを作成します。
ステップ10—アプリケーションをテストするための安全なトンネルを作成する
サービスワーカーは、を除くすべてのサイトで機能するために安全な接続を必要とします localhost
これは、接続が乗っ取られ、応答がフィルタリングおよび作成される可能性があるためです。 このため、ngrokを使用してサーバー用の安全なトンネルを作成します。
2番目のターミナルウィンドウを開き、ホームディレクトリにいることを確認します。
- cd ~
前提条件でクリーンな18.04サーバーを使用して開始した場合は、インストールする必要があります unzip
:
- sudo apt update && sudo apt install unzip
ngrokをダウンロード:
- wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
- unzip ngrok-stable-linux-amd64.zip
動く ngrok
に /usr/local/bin
、にアクセスできるように ngrok
ターミナルからのコマンド:
- sudo mv ngrok /usr/local/bin
最初のターミナルウィンドウで、プロジェクトディレクトリにいることを確認し、サーバーを起動します。
- cd ~/djangopush
- python manage.py runserver your_server_ip:8000
アプリケーションの安全なトンネルを作成する前に、これを行う必要があります。
2番目のターミナルウィンドウで、プロジェクトフォルダーに移動し、仮想環境をアクティブ化します。
- cd ~/djangopush
- source my_env/bin/activate
アプリケーションへの安全なトンネルを作成します。
- ngrok http your_server_ip:8000
次の出力が表示されます。これには、安全なngrokURLに関する情報が含まれています。
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://ngrok_secure_url -> 203.0.113.0:8000
Forwarding https://ngrok_secure_url -> 203.0.113.0:8000
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
をコピーします ngrok_secure_url
コンソール出力から。 あなたはそれをのリストに追加する必要があります ALLOWED_HOSTS
あなたの中で settings.py
ファイル。
別のターミナルウィンドウを開き、プロジェクトフォルダーに移動して、仮想環境をアクティブ化します。
- cd ~/djangopush
- source my_env/bin/activate
を開きます settings.py
ファイル:
- nano ~/djangopush/djangopush/settings.py
のリストを更新します ALLOWED_HOSTS
ngrokセキュアトンネルを使用:
...
ALLOWED_HOSTS = ['your_server_ip', 'ngrok_secure_url']
...
安全な管理ページに移動してログインします。 https://ngrok_secure_url/admin/
. 次のような画面が表示されます。
この画面にDjango管理者ユーザー情報を入力します。 これは、前提条件の手順で管理インターフェースにログインしたときに入力した情報と同じである必要があります。 これで、プッシュ通知を送信する準備が整いました。
訪問 https://ngrok_secure_url
ブラウザで。 通知を表示する許可を求めるプロンプトが表示されます。 許可ボタンをクリックして、ブラウザにプッシュ通知を表示させます。
記入済みのフォームを送信すると、次のような通知が表示されます。
注:通知を送信する前に、サーバーが実行されていることを確認してください。
通知を受け取った場合、アプリケーションは期待どおりに機能しています。
サーバー上でプッシュ通知をトリガーし、サービスワーカーの助けを借りて、通知を受信して表示するWebアプリケーションを作成しました。 また、アプリケーションサーバーからプッシュ通知を送信するために必要なVAPIDキーを取得する手順も実行しました。
結論
このチュートリアルでは、通知APIを使用して、ユーザーをサブスクライブしてプッシュ通知をサブスクライブし、サービスワーカーをインストールし、プッシュ通知を表示する方法を学習しました。
クリックしたときにアプリケーションの特定の領域を開くように通知を構成することで、さらに先に進むことができます。 このチュートリアルのソースコードはここにあります。