Web PUSH通知をすばやく簡単に

こんにちは この短い記事では、サイトにプッシュ通知を迅速かつ簡単に設定する方法を説明します。 この記事は網羅的なガイドではありませんが、今後の研究の出発点となることを願っています。







このトピックに関する多くの情報はインターネット上にありますが、断片化され、さまざまなリソースに散らばり、Java、C ++、Pythonの例を含むモバイルデバイスの通知と混ざっています。 私たちは、Web開発者としてJavaScriptに興味があります。 この記事では、すべての必要かつ有用な情報を蓄積しようとします。







Web PUSH通知







プッシュ通知が何であるかを既に知っていると思います 、それでも主なことについて簡単に説明します。







サイトに入るユーザーは、そこからデータを取得します。 これは便利で安全ですが、インターネットリソースの開発に伴い、ユーザーが要求するのを待たずに、ユーザーに情報を迅速に配信する必要があります。 そのため、サーバーからクライアントへのデータのプッシュ配信のテクノロジーが登場しました。







重要です



プッシュ通知は、サイトにHTTPSがある場合にのみ機能します。

有効なSSL証明書がないと、機能しません。 したがって、HTTPSのサポートがまだない場合は、それを実行する時間です。 Let's Encryptの使用をお勧めします。

ローカルホストで実行するには、 トリックに頼る必要があります。 Github Pagesでスクリプトをテストしました。


目次





良い通知



プッシュ通知は広告メール用ではないことをすぐに予約したいと思います。 特定のユーザーが本当に必要とするものと、彼が本当に迅速に応答する必要があるものだけを送信する必要があります。







良い例:









悪い例:









悪い例でも通知が必要ですが、迅速に対応する必要はありません。 これらの通知はメールで送信できます。 一般に、プッシュ通知は制御できないさまざまな理由でユーザーに届かない可能性があるため、すべての重要な通知を電子メールで複製することをお勧めします。 もう1つの重要な要素は、イベントの関連性です。 これについては後で説明します。 読むことをお勧めします:









羊に戻りましょう。 それでは、どのように機能しますか? まず、少しの理論。







理論



初心者の中では、プッシュ通知は特別なリソースを実装する必要のない単純な技術であるという意見があります。 実際には、これはテクノロジーのプール全体です。







はじめに、それがどのように機能するかについての小さなアウトライン( アニメーションレイアウト ):







PUSH通知での相互作用のスキーム







  1. サーバーはユーザーにページを提供します。
  2. クライアントはメッセージサーバーに接続し、IDを登録および受信します。
  3. クライアントは受信したIDをサーバーに送信し、サーバーはデバイスIDを使用して特定のユーザーを特定のデバイスにバインドします。
  4. サーバーは、以前に受信したIDを使用して、メッセージサーバーを介してクライアントにメッセージを送信します。


残念ながら、誰がどのようにデバイスIDを作成し、メッセージサーバーが特定のデバイスにどのように接続されているかを知ることができませんでした。 GoogleのFirebase Cloud Messagingメッセージサーバーとそのライブラリを使用しました。 残念ながら、サーバーに置き換えることができるかどうか、およびその方法を理解できませんでした。







楽しい事実



最初に、使用されるメッセージを送信するには:

クラウドからデバイスへのメッセージング



その後、次のものに置き換えられました。

Googleクラウドメッセージング



そして、再び次のように変更されました。

Firebaseクラウドメッセージング



次は何だろう。


クライアント側で何が起こりますか?





通知権をリクエストする







ご注意



Google 、スイッチを使用して通知を購読および購読解除することをお勧めします。 したがって、通知サブスクリプションプロセスの開始は、サイトからではなく、ユーザーから行われます。

各着信ユーザーの通知にサインアップを強制することは悪い習慣です。 そうしないでください。

すべて非常に複雑に見えますが、サーバー上ではすべてが単純ではありません。







サーバー側の問題





練習する



最後に、最も重要なことに移りました。 前述のとおり、 Firebase Cloud Messagingをメッセージサーバーとして使用するため、Firebaseでプロジェクトを登録および作成することから始めます。







ここではすべてが簡単です:









あなたはまだ設定を掘り下げてアクセス権の分離で遊ぶことができますが、一般に、Firebaseサイトでの作業は終わりました。







クライアントの作成を開始する



プッシュ通知を受信するService Workerを作成することから始めましょう。

次の内容のfirebase-messaging-sw.jsファイルを作成します。







// firebase-messaging-sw.js importScripts('https://www.gstatic.com/firebasejs/3.6.8/firebase-app.js'); importScripts('https://www.gstatic.com/firebasejs/3.6.8/firebase-messaging.js'); firebase.initializeApp({ messagingSenderId: '<SENDER_ID>' }); const messaging = firebase.messaging();
      
      





どこで









重要なお知らせ



Service Workerファイルはfirebase-messaging-sw.jsと正確に呼ばれる必要があり、プロジェクトのルートに配置する必要があります 。つまり、 https: //example.com/firebase-messaging-sw.jsで利用可能です。 このファイルへのパスは Firebase ライブラリにハードコーディングされています

作成されたコードは、通知を表示するのに十分です。 追加機能については少し後で説明します。 次に、Firebaseライブラリとサブスクリプションスクリプトをページテンプレートに追加します。







 <script type="text/javascript" src="//www.gstatic.com/firebasejs/3.6.8/firebase.js"></script> <script type="text/javascript" src="/firebase_subscribe.js"></script>
      
      





通知をサブスクライブするボタンをページに追加します







 <button type="button" id="subscribe">  </button>
      
      





通知を購読する







 // firebase_subscribe.js firebase.initializeApp({ messagingSenderId: '<SENDER_ID>' }); //    // ,      Firebase,      if ('Notification' in window) { var messaging = firebase.messaging(); //      //        if (Notification.permission === 'granted') { subscribe(); } //  ,       //    $('#subscribe').on('click', function () { subscribe(); }); } function subscribe() { //      messaging.requestPermission() .then(function () { //  ID  messaging.getToken() .then(function (currentToken) { console.log(currentToken); if (currentToken) { sendTokenToServer(currentToken); } else { console.warn('   .'); setTokenSentToServer(false); } }) .catch(function (err) { console.warn('    .', err); setTokenSentToServer(false); }); }) .catch(function (err) { console.warn('      .', err); }); } //  ID   function sendTokenToServer(currentToken) { if (!isTokenSentToServer(currentToken)) { console.log('   ...'); var url = ''; //       ID  $.post(url, { token: currentToken }); setTokenSentToServer(currentToken); } else { console.log('    .'); } } //  localStorage   , //       function isTokenSentToServer(currentToken) { return window.localStorage.getItem('sentFirebaseMessagingToken') == currentToken; } function setTokenSentToServer(currentToken) { window.localStorage.setItem( 'sentFirebaseMessagingToken', currentToken ? currentToken : '' ); }
      
      





以上です。 これは、プッシュ通知を受信するために必要なすべてのコードです。







サーバーから通知を送信する



一般に、通知の送信は次のようになります。







 POST /fcm/send HTTP/1.1 Host: fcm.googleapis.com Authorization: key=YOUR-SERVER-KEY Content-Type: application/json { "notification": { "title": "", "body": "  21:00", "icon": "https://eralash.ru.rsz.io/sites/all/themes/eralash_v5/logo.png?width=40&height=40", "click_action": "http://eralash.ru/" }, "to": "YOUR-TOKEN-ID" }
      
      





どこで









順番にすべてのフィールド:









お知らせ






これは、1人の受信者に1つの通知を送信する例です。 1つの通知を複数の受信者に一度に送信できます。 一度に最大1000人の受信者。







 { "notification": { "title": "", "body": "  21:00", "icon": "https://eralash.ru.rsz.io/sites/all/themes/eralash_v5/logo.png?width=192&height=192", "click_action": "http://eralash.ru/" }, "registration_ids": [ "YOUR-TOKEN-ID-1", "YOUR-TOKEN-ID-2" "YOUR-TOKEN-ID-3" ] }
      
      





メッセージサーバーからの応答の例:







Chrome通知
 { "multicast_id": 6407277574671070000, "success": 1, "failure": 0, "canonical_ids": 0, "results": [ { "message_id": "0:1489072146895227%e609af1cf9fd7ecd" } ] }
      
      





FireFoxへの通知の送信
 { "multicast_id": 7867877497742898000, "success": 1, "failure": 0, "canonical_ids": 0, "results": [ { "message_id": "https://updates.push.services.mozilla.com/m/gAAAAABYwWmlTCKje5OLwedhNUQr9LbOCmZ0evAF9HJBnR-v7DF2KEkZY3zsT8AbrqB6JfJO6Z6vsotLJMmiIvJs9Pt1Q9oc980BRX2IU1-jlzRLIhSVVBLo2i80kBvTMYadVAMIlSIyFkWm-qg_DfLbenlO9z1S4TGMJl0XbN5gKMUlfaIjnX2FBG4XsQjDKasiw8-1L38v" } ] }
      
      





通知送信エラー
 { "multicast_id": 8165639692561075000, "success": 0, "failure": 1, "canonical_ids": 0, "results": [ { "error": "InvalidRegistration" } ] }
      
      





エラーコードの完全なリスト。







特定のプログラミング言語に縛られることはありません。サンプルを簡単にするために、cURL拡張機能付きのPHPを使用します。 通知送信スクリプトは、コンソールから実行する必要があります。







 #!/usr/bin/env php <?php $url = 'https://fcm.googleapis.com/fcm/send'; $YOUR_API_KEY = ''; // Server key $YOUR_TOKEN_ID = ''; // Client token id $request_body = [ 'to' => $YOUR_TOKEN_ID, 'notification' => [ 'title' => '', 'body' => sprintf('  %s.', date('H:i')), 'icon' => 'https://eralash.ru.rsz.io/sites/all/themes/eralash_v5/logo.png?width=192&height=192', 'click_action' => 'http://eralash.ru/', ], ]; $fields = json_encode($request_body); $request_headers = [ 'Content-Type: application/json', 'Authorization: key=' . $YOUR_API_KEY, ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers); curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); $response = curl_exec($ch); curl_close($ch); echo $response;
      
      





messaging.onMessage



messaging.onMessage



ハンドラーは、落とし穴のカテゴリーに属しているため、特に言及する価値があります。 Firebaseの例では、このハンドラーを使用した例を見ていません。 私はFluorescentHallucinogenによってそれについて話されました、彼に特別な感謝をしますが、彼はその使用のいくつかの特徴に言及しませんでした。







これはどのようなハンドラーで、どのように機能しますか。 ドキュメントから、プッシュ通知を受信するとこのハンドラーが呼び出され、その時点で通知が送信されたサイト(ネイティブソリューションを使用したい人は実装例を参照できます)にあることがわかります。 この機能は、美しいモーダルなどを作成してページに通知を表示できるという点で非常に便利です。 そのようなニーズはないので、 標準の通知を表示します。







 if ('Notification' in window) { var messaging = firebase.messaging(); messaging.onMessage(function(payload) { console.log('Message received. ', payload); new Notification(payload.notification.title, payload.notification); }); // ... } // ...
      
      





すべてがシンプルに思えますが、落とし穴があります。 問題は、モバイルデバイスでは、Notificationコンストラクターの使用が禁止されていることです。 この問題を解決するには、 ServiceWorkerRegistration.showNotification()



を使用する必要があり、この場合のハンドラーは次のようになります。







 // ... messaging.onMessage(function(payload) { console.log('Message received. ', payload); //   ServiceWorker   navigator.serviceWorker.register('messaging-sw.js'); //           Notification.requestPermission(function(result) { if (result === 'granted') { navigator.serviceWorker.ready.then(function(registration) { //      return registration.showNotification(payload.notification.title, payload.notification); }).catch(function(error) { console.log('ServiceWorker registration failed', error); }); } }); }); // ...
      
      





通知は、モバイルデバイスで機能するようになりました。 それはすべてのように見えますが、そうではありません。 一部保証にもかかわらず、ServiceWorkerは空にしないでください。 ユーザーがクリックする必要のあるページをクリックするようにします。 これを行うには、ServiceWorkerに通知クリックハンドラーを追加する必要があります。







click_action



プロパティにアクセスするための通知設定を保存します。







 // ... navigator.serviceWorker.ready.then(function(registration) { payload.notification.data = payload.notification; //   registration.showNotification(payload.notification.title, payload.notification); }).catch(function(error) { console.log('ServiceWorker registration failed', error); }); // ...
      
      





ServiceWorkerで通知クリックを処理します。







 // messaging-sw.js self.addEventListener('notificationclick', function(event) { const target = event.notification.data.click_action || '/'; event.notification.close(); //            //      ,     event.waitUntil(clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function(clientList) { // clientList -  !? for (var i = 0; i < clientList.length; i++) { var client = clientList[i]; if (client.url == target && 'focus' in client) { return client.focus(); } } //    return clients.openWindow(target); })); });
      
      





TTLおよび追加の通知制御



通知のための重要なプロパティは、その関連の時間かもしれません。 それはビジネスプロセスに依存します。 デフォルトでは、通知の有効期間は4週間です。 これは、この種の通知には多くの機能があります。 たとえば、「お気に入りの番組は15分後に開始します」という通知は、15分間関連しています。 その後、メッセージは関係ないため表示されません。 0〜2419200秒の値を持つtime_to_live



プロパティは、ライフタイムの制御を担当します。 詳細はドキュメントをご覧ください。 指定されたTTLを持つメッセージは次のようになります。







 { "notification": { "title": "", "body": "  15 ", "icon": "https://eralash.ru.rsz.io/sites/all/themes/eralash_v5/logo.png?width=192&height=192", "click_action": "http://eralash.ru/" }, "time_to_live": 900, "to": "YOUR-TOKEN-ID" }
      
      





「お気に入りの番組は15分で開始します」などのメッセージは15分間関連していますが、送信後1分以内に不正確になります。 転送は15分後ではなく14分後に開始されるため、このような状況はクライアント側で制御する必要があります。







これを行うには、サーバーから送信されるメッセージを変更します。







 { "data": { "title": "", "time": 1489006800, "icon": "https://eralash.ru.rsz.io/sites/all/themes/eralash_v5/logo.png?width=192&height=192", "click_action": "http://eralash.ru/" }, "time_to_live": 900, "to": "YOUR-TOKEN-ID" }
      
      





notification



フィールドがdata



変更されていることに注意してください。 これで、デフォルトのFirebaseハンドラーは呼び出されなくなり、独自にこれを行う必要があります。 firebase-messaging-sw.js



の最後に次の行を追加しfirebase-messaging-sw.js









 //     messaging.setBackgroundMessageHandler(function(payload) { if (typeof payload.data.time != 'undefined') { var time = new Date(payload.data.time * 1000); var now = new Date(); if (time < now) { //     return null; } var diff = Math.round((time.getTime() - now.getTime()) / 1000); //      //    : "  14 ,  21:00" payload.data.body = '  ' + Math.round(diff / 60) + ' ,  ' + time.getHours() + ':' + (time.getMinutes() > 9 ? time.getMinutes() : '0' + time.getMinutes()) ; } //  data       payload.data.data = payload.data; //   return self.registration.showNotification(payload.data.title, payload.data); }); //      self.addEventListener('notificationclick', function(event) { //       const target = event.notification.data.click_action || '/'; event.notification.close(); //            //      ,     event.waitUntil(clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function(clientList) { // clientList -  !? for (var i = 0; i < clientList.length; i++) { var client = clientList[i]; if (client.url == target && 'focus' in client) { return client.focus(); } } //    return clients.openWindow(target); })); });
      
      





このような簡単な方法で、通知を完全に制御できました。 最も興味深いのは、ユーザーに自分のタイムゾーンで通知時間を表示することです。 これは、世界中で機能するサービスや、母ロシアのようにタイムゾーンが広範囲に広がる地域に当てはまります。







おわりに



さて、悲しいことについて話しましょう。 テクノロジーのすべての魅力にもかかわらず、多くの欠点があります。







  1. 主な問題は、いつものように、 ブラウザのサポートです。 Chrome、Firefox、Operaの最新バージョンでは完全にサポートされています。 IE、Safari、Opera Mini、UC Browser、Dolphin、およびその他の兄弟は除外されています。 ただし、モバイル版のChrome、Firefox、Operaブラウザーでは機能します。
  2. オープンサイトと実行中のService Workerは、メッセージ配信を保証しません。 ただし、通知は閉じたブラウザーで発生する場合があります。


Firebaseライブラリは多くの秘密を隠しており、その研究はいくつかの質問に対する答えを提供する可能性がありますが、これはこの記事の範囲外です。







プレイする



GitHub Pagesプロジェクト



Service Workerを起動するにはHTTPSが必要なので、最も簡単な解決策はプロジェクトをGitHub Pagesに置くことでした。







プロジェクトは、 https//github.com/peter-gribanov/serviceworkerで入手できます

プロジェクトのソース: https : //peter-gribanov.github.io/serviceworker/













プロジェクトは、通知を送受信するための完全なアプリケーションです。 通知を受信するには、次のことが必要です。









HTTPリクエストを送信するには、任意のツールを使用して通知を送信できます。 cURLを使用できます。Chrome用のPostmanアプリの方が好きです。







要求は、前述のとおりです。







 POST /fcm/send HTTP/1.1 Host: fcm.googleapis.com Authorization: key=AAAAaGQ_q2M:APA91bGCEOduj8HM6gP24w2LEnesqM2zkL_qx2PJUSBjjeGSdJhCrDoJf_WbT7wpQZrynHlESAoZ1VHX9Nro6W_tqpJ3Aw-A292SVe_4Ho7tJQCQxSezDCoJsnqXjoaouMYIwr34vZTs Content-Type: application/json { "data": { "title": "", "body": "  21:00", "icon": "https://eralash.ru.rsz.io/sites/all/themes/eralash_v5/logo.png?width=192&height=192", "click_action": "http://eralash.ru/" }, "to": "YOUR-TOKEN-ID" }
      
      





どこで









以上です。 私たちは通知を受け取り、人生を楽しみます。







参照資料



関連リンクリスト


2018-06-09に更新



いくつかの「機能」が通知の作業で見つかりました。







重複した通知



「重複した通知を修正する方法は?」という質問で何度か尋ねられました







この問題は、複数のタブで同時に通知を送信するサイトを開くと明らかになります。 この場合、Service Workerは両方のタブに通知を送信し、 messaging.onMessageメソッドが両方のタブで起動します。 この問題は、私のデモプロジェクトで確認できます。







この問題を解決するには、 messaging.onMessage



メソッドで、通知がすでに別のタブに表示されていることを知る必要があります。 localStorage



を単一のストアとして使用localStorage



、通知量のハッシュによって通知を識別するか、一意のIDを割り当てることができます。 localStorage



ゴムでlocalStorage



なく、既に表示されている通知のIDはしばらくしてからクリーンアップする必要があることに注意してください。







これらの目的のためにpamelafox / lscacheライブラリをお勧めできます。

問題を解決する別の方法がある場合は、コメントを書いてください。







通知の写真



今日、 CTteroristのユーザーが私に目を向けました。彼は通知に画像が表示されていないことに気付きました。













少しテストして、私はそれを理解することができました。 image



フィールドは他の通知パラメーターとともにFirebaseに送信されますが、 image



フィールドはFirebaseから返されません。 問題は非常に簡単に解決されます。 data



フィールドでカードを送信し、通知表示ハンドラーでdata



から画像を抽出し、通知の所定の場所に挿入できます。







つまり、このフォームでメッセージを送信すると、Firebaseは画像を失います。







 { "notification": { "title": "Bubble Nebula", "body": "It's found today at 21:00", "icon": "https://peter-gribanov.github.io/serviceworker/Bubble-Nebula.jpg", "image": "https://peter-gribanov.github.io/serviceworker/Bubble-Nebula_big.jpg", "click_action": "https://www.nasa.gov/feature/goddard/2016/hubble-sees-a-star-inflating-a-giant-bubble" }, "to": "YOUR-TOKEN-ID" }
      
      





ただし、画像をdata



転送すると、画像が表示されdata









 { "data": { "title": "Bubble Nebula", "body": "It's found today at 21:00", "icon": "https://peter-gribanov.github.io/serviceworker/Bubble-Nebula.jpg", "image": "https://peter-gribanov.github.io/serviceworker/Bubble-Nebula_big.jpg", "click_action": "https://www.nasa.gov/feature/goddard/2016/hubble-sees-a-star-inflating-a-giant-bubble" }, "to": "YOUR-TOKEN-ID" }
      
      





通知表示ハンドラーは、上記の例と同じです。







 messaging.onMessage(function(payload) { console.log('Message received. ', payload); //   ServiceWorker   navigator.serviceWorker.register('firebase-messaging-sw.js'); //           Notification.requestPermission(function(result) { if (result === 'granted') { navigator.serviceWorker.ready.then(function(registration) { //       TTL  .. //   data payload.data.data = JSON.parse(JSON.stringify(payload.data)); registration.showNotification(payload.data.title, payload.data); }).catch(function(error) { console.log('ServiceWorker registration failed', error); }); } }); });
      
      





同様に、Service Worker( firebase-messaging-sw.js









 messaging.setBackgroundMessageHandler(function(payload) { console.log('Handling background message', payload); //       TTL  .. //   data payload.data.data = JSON.parse(JSON.stringify(payload.data)); registration.showNotification(payload.data.title, payload.data); });
      
      






All Articles