【Other】【CSS / jQuery / javascript】PWAでプッシュ通知を実装してみる(4)実際にプッシュ通知を送信・受信する
- 2019/08/06 14:19
- PWA
- プッシュ通知
- Firebase
- Cloud Messaging
- Cloud Firestore
- Node.js
- 10
前回までの記事では、プッシュ通知に必要なトークンを閲覧者に発行させ、購読状況とともにデータベースに記録させるところまでやりました。
今回からはデータベースに登録させた情報を元に、実際にプッシュ通知を送信・受信できるところまでやってみます。
Firebase Cloud Messaging でプッシュ通知を受信する
Google Firebase Cloud Messaging(FCM)でプッシュ通知許可時・購読時に発行されるトークン処理については前回までの記事で解説しているので、今回は受信・送信機能部分を解説します。
PWA化されたサイトでプッシュ通知を受信するにはserviceworker.jsへの記載が必要ですが、FCMを利用する時は、serviceworkerの名前をfirebase-messaging-sw.jsにする必要があります。
既にserviceworkerを利用している場合、serviceworkerの記述をfirebase-messaging-sw.jsに統合する必要があります。
複数のserviceworkerを設置する方法はなくはないようですが、分散させるよりは統合した方が処理が散漫にならず良いと思います。
キャッシュ機能についてのserviceworkerの記載は既に解説していますが、それに追加するプッシュ通知受信に関する記載は下記の通りです。
// Firebase利用準備 importScripts('https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js'); importScripts('https://www.gstatic.com/firebasejs/6.2.0/firebase-messaging.js'); firebase.initializeApp({ 'messagingSenderId': 'YOUR-SEND-ID' }); const messaging = firebase.messaging(); // フォアグラウンドでのプッシュ通知受信 messaging.onMessage(function(payload) { var notificationTitle = payload.data.title; // タイトル var notificationOptions = { body: payload.data.body, // 本文 icon: '/pwa_512.png', // アイコン click_action: 'https://xxxx.sample.com/' // 飛び先URL }; if (!("Notification" in window)) { // ブラウザが通知機能に対応しているかを判定 } else if (Notification.permission === "granted") { // 通知許可されていたら通知する var notification = new Notification(notificationTitle,notificationOptions); } }); // バックグラウンドでのプッシュ通知受信 messaging.setBackgroundMessageHandler(function(payload) { console.log('[firebase-messaging-sw.js] Received background message ', payload); // Customize notification here var notificationTitle = payload.notification.title; // タイトル var notificationOptions = { body: payload.notification.body, // 本文 icon: payload.notification.icon, // アイコン }; return self.registration.showNotification(notificationTitle, notificationOptions); });
送信された通知の内容はjson形式で受け取ります。
受け取ったjsonをプッシュ通知に変換してブラウザの状態にあわせて表示します。
参考URL
Firebase ドキュメント:JavaScript クライアントでメッセージを受信する
プッシュ通知受信テスト
Google Chromeを使えば、プッシュ通知の受信テストができます。
Firebase Cloud Messaging でプッシュ通知を送信する
プッシュ通知を受信する準備ができても、肝心の送信手続きについては全くの未知数でした。
いろんなサイトを見ても、「サーバーからcURLコマンドを叩けばおk」とかしか書いてなくて、「サーバーってなんだ? サイトを置いてあるWEBサーバーのこと??」とか「cURLってなんだよ」「どうやって叩くのよ」とかいろんな疑問点が噴出していました。
プッシュ通知を送信するためにしたいこと
プッシュ通知を送信するためにしたいことをまとめておきます。
- データベースに登録されたトークンをトピックに紐付ける
- 通知メッセージの内容は送信する度に変更する
- 通知は自動で送信するのではなく通知したいタイミングで手動で送信する
トピックメッセージング(トピック)とは、Firebase Cloud Messagingの機能で複数のトークンに対し一括で通知メッセージを送信する仕組みです。
同じような機能でデバイスグループメッセージングという機能がありますが、こちらはさまざまなスマートフォンモデルに応じて異なるメッセージを送信する場合に使用するものです。
今回はデバイスに応じた処理を行う予定はないので、トピックを利用することにします。
参考URL
Firebase ドキュメント:複数のデバイスにメッセージを送信する
Firebase ドキュメント:ウェブ / JavaScript でデバイス グループにメッセージを送信する
プッシュ通知の送信機能はアプリサーバーに持たせる
当初は、まさに素人考えですが、プッシュ通知を送信するためのPHPなりなんなりをWEB上に上げて、もしくはサイトに組み込んで、そこからプッシュ通知を送信できればベストだなと思っていました。
しかし、Firebase ドキュメントを読むとサーバーキーの機密性を維持するため、プッシュ通知の送信やトピックへの登録などのリクエストをクライアントから送信しないようにと書いてありました。
プッシュ通知の送信に必要なサーバーキー・トークン一覧などを誰でも閲覧可能にすると、たしかに悪意のあるユーザーから偽装されたプッシュ通知を送信される可能性があります。
よって、プッシュ通知はアプリサーバーから送信することにしました。
アプリサーバーといっても、
送信ロジック(認証、送信リクエストの作成、応答の処理などを行うロジック)を作成するために、Admin SDK を使用するか、またはサーバー プロトコルの 1 つを使用するかを決定します。次に、信頼できる環境でロジックを構築します。クライアント アプリケーションからのアップストリーム メッセージングを使用する場合には、XMPP を使用する必要があります。XMPP で必要となる永続的な接続が Cloud Functions ではサポートされないことに注意してください。
Firebase ドキュメント:Firebase Cloud Messaging
ということなので、コマンドプロンプトにNode.jsをインストールしてFirebase Admin SDKを利用することにします。
Firebase Admin SDKを利用するにはNode.jsだけではなくJAVA・Python・Go・C#でもOKなようですが、Node.jsが一番なんでもできそうだったのでNode.jsにしました。
参考URL
Firebase ドキュメント:サーバーに Firebase Admin SDK を追加する
コマンドプロンプトにNode.jsをインストールする
そもそもNode.jsってなにかっていうと、サーバーでJavaScriptを利用できるようにするためのもののようです。
FirebaseはJavaScriptで構成されているので、サーバーでNode.jsが必要だというのもうなずけます。
参考URL
エンジニアの入り口:初心者向け!3分で理解するNode.jsとは何か?
Qiita:Windows版 Node.js環境構築方法まとめ
Node.jsのインストールされたコマンドプロンプトにFirebase Admin SDKを追加する
- Windowsでサーバーアプリとして利用するディレクトリをどこかに作っておく。
例えば[C:\webpush]など適当でいいと思います。 - Node.js command promptを起動して[1]で作成したディレクトリに移動する。
- 下記コマンドを実行する
$ npm install firebase-admin --save
- コマンドプロンプトが色々頑張ってこんな感じの画面になる
- [1]で作成したディレクトリを見に行くと、なんかファイルが出来ている
- firebaseのコンソールから[設定]>[サービスアカウント]を開く
- [新しい秘密鍵の生成]をクリックする
- [キーを生成]をクリックする
- 秘密鍵ファイルのDLが開始するので[1]とは別のディレクトリに保存する。
別に[1]と同じでもいいけど、要は公開されないようにすればOK。
[1]をリポジトリにしてGitで管理する場合などは公開されないように注意すること。
生成した秘密鍵への紐付けは、コード内に記載して行うこともできます(自分は今回そうしています)が、システムの環境変数を設定する方法もあります。
環境変数を設定したほうが安全ですが、複数のプロジェクトを扱う場合、新しいセッションを開く場合はセッションを再度設定しなければなりません。
環境変数[GOOGLE_APPLICATION_CREDENTIALS
]への設定方法は下記Firebaseドキュメントを参考にしてください。
Firebase ドキュメント:サーバーに Firebase Admin SDK を追加する-SDKの追加
Firebase ドキュメント:サーバーに Firebase Admin SDK を追加する-SDK の初期化
プッシュ通知送信処理用のJS
Firebase Admin SDK のインストールが終わったら、これを使ってサーバー側でのプッシュ通知送信処理を作成します。
今回はJavaScriptで作成しました。
下記にサンプルを用意したので、[send-push.js]などのように名前をつけて、Firebase Admin SDKをインストールしたフォルダに保存してください。
// 設定 var admin = require("firebase-admin"); var serviceAccount = require("./YOUR-SERVICE-ACCOUNT.json");//Admin SDK利用のため作成した秘密鍵の場所 admin.initializeApp({ credential: admin.credential.cert(serviceAccount), databaseURL: "https://XXXXXXXXXXXXXXXXXXXXXXXXXX.firebaseio.com"//[Firebaseコンソール→設定→サービスアカウント]で確認できるデータベースのURL }); // 宛先のトピックを指定 var topic = 'XXXXXX'; var db = admin.firestore(); // DBから取得したトークンを配列に格納 db.collection('users').get() .then((snapshot) => { const entryTokens = []; const removeTokens = []; snapshot.forEach((doc) => { var data = doc.data(); if(data.subscribe == true){ entryTokens.push(data.token); } else { removeTokens.push(data.token); } }); // トピック購読処理 admin.messaging().subscribeToTopic(entryTokens, topic) .then(function(response) { // See the MessagingTopicManagementResponse reference documentation // for the contents of response. console.log('Successfully subscribed to topic:', response); }) .catch(function(error) { console.log('Error subscribing to topic:', error); }); // トピック購読解除 admin.messaging().unsubscribeFromTopic(removeTokens, topic) .then(function(response) { // See the MessagingTopicManagementResponse reference documentation // for the contents of response. console.log('Successfully unsubscribed to topic:', response); }) .catch(function(error) { console.log('Error subscribing to topic:', error); }); }) .catch((err) => { console.log('Error getting documents', err); }); // 通知を定義 var message = { topic: topic, webpush: { notification: { title: "タイトル", body: "通知メッセージ", requireInteraction: "true", badge: "https://YOUR-DOMAIN/pwa_512.png", icon: "https://YOUR-DOMAIN/pwa_512.png", }, fcm_options: { link: "https://YOUR-DOMAIN/" } } }; // 通知を送る admin.messaging().send(message) .then((response) => { // Response is a message ID string. console.log('Successfully sent message:', response); }) .catch((error) => { console.log('Error sending message:', error); });
変更箇所はいくつかあります。
最初の設定部分は、[firebaseコンソール>設定>サービスアカウント]でも確認できます。
Admin SDK利用のため作成した秘密鍵の場所を明示しておいてください。
送信先トピック名はなんでも構いません。
わかりやすいものを設定しましょう。
Usersテーブルから登録済みトークン一覧を取得し、WEB通知の購読状況に応じてトピックを購読したり削除したりします。
最新のトピック購読者リストが更新できたら、通知を送信します。
通知内容はここで編集します。fcm_options
の[link]は通知をクリックした時の飛び先URLです。
受信時のserviceworker側でなく、送信時に色々設定出来たほうがよいと思います。
最後の部分でプッシュ通知を送信します。
ワンポイント
ウェブアプリの場合トピックや送信処理をFirebaseコンソールで操作できない
色々調べたところ、Firebaseコンソールでトピックの登録についてやプッシュ通知の送信自体を操作できるようなことが書いてあったのですが、それはAndroidやiOSだけの話で、どうやらウェブアプリではCloud Messagingのコンソールが利用できないようです。
残念。将来的にできるようになることを期待したいです。
参考URL
Firebase ドキュメント:ウェブや JavaScript でメッセージをトピックに送信する
プッシュ通知の送信を行う
ようやくプッシュ通知の送信を行います。
上記作成した[send-push.js]をNode.jsで実行します。
- Node.js command promptを起動する
- [push.js]が保存されているフォルダに移動する。
- 下記コマンドを実行する
node send-push.js
- プッシュ通知送信結果が表示される。
以上でプッシュ通知の送信作業は完了です。
お疲れさまでした。
最後に
いかがでしたでしょうか。プッシュ通知をPWAでできるようになったことで、様々なサイトでオリジナルの通知を送信できるようになりました。
購読処理の設定を変更して好きなテーマについてのみ通知を受け取れるようにしたり、アプリサーバーの送信処理を変更して時間を指定して通知を送ったりすることもできるようになりそうです。
初心者が初心者なりになんとか実現にこぎつけただけのメモですので、もっといい方法なりやり方があれば教えてください。