Firebase Cloud Messaging の Web Push を試してみた

広告システム開発部の佐藤禎章です。

今回は、Firebaseサービスの一つである Cloud Messaging を使用して、ブラウザへ Push 通知を送る仕組みを揃えてみました。

試しやすく単純な仕組みに出来るので、 node を使用して全部 JavaScript で書いてあります。

node : v4.6.0
npm : 2.15.9

今回作成するものの全体は、下記 Github リポジトリに投入してあります。 これをそのまま clone して、API Key などの設定を投入すれば、そのまま使えます。

https://github.com/medi-y-sato/fcm-test-node

前提知識 : Service Worker とは

今回の Push 送受信には Service Worker というものを使用します。 これは何かとざっくり言うと、 Webページとは別にバックグランドで動くスクリプト です。

オフライン時でも動作する Web アプリを作るため仕組みとして整備されてきたもので、リクエストのキャッシュや同期などの API が含まれています。

その中に Push 通知を受ける( Service Worker が Push 通知の通信を契機に Active になり、処理を行う)機能があるので、今回はそれを使用します。

詳しく知りたい場合は、下記などを参照下さい。

Service Worker の紹介 By Matt Gaunt

Firebase での下準備

Push 通知の仕組みとして Firebase を使用するので、 https://firebase.google.com/ からプロジェクトを一つ作成して下さい。

出来上がったプロジェクトの [プロジェクトの設定]から、[クラウドメッセージング]タブを選択し、サーバキーと送信者IDを控えておいて下さい。

また、[ウェブアプリに Firebase を追加]から取得できる Javascript のコードも、同様に控えておいて下さい。

クライアントサイドの設定

クライアントサイドの設定を入れていきます。

index.html

通知を受けるブラウザで開く HTML / JavaScript になります。

index.html の全体はこちらになります。

https://github.com/medi-y-sato/fcm-test-node/blob/master/index.html

設定の投入が必要です、下記。

<script src="https://www.gstatic.com/firebasejs/3.4.1/firebase.js"></script>
<script>
  var config = {
    apiKey: "<<CHANGE HERE>>",
    authDomain: "<<CHANGE HERE>>",
    databaseURL: "<<CHANGE HERE>>",
    storageBucket: "<<CHANGE HERE>>",
    messagingSenderId: "<<CHANGE HERE>>"
  };
  firebase.initializeApp(config);
</script>

[ウェブアプリに Firebase を追加]の所で控えたコードを、そのまま貼り付けます。 これで、このページ内で Firebase のサービスが使用できるようになります。

<link rel="manifest" href="manifest.json">

Web アプリの設定を記述する manifest ファイルを指定します。 中身については後述。

if ('serviceWorker' in navigator) {
  console.log('Service Worker is supported');
  navigator.serviceWorker.register('./serviceworker.js').then(function(reg) {
    console.log('Service Worker is ready :^)', reg);
    reg.pushManager.subscribe({userVisibleOnly: true}).then(function(sub) {
      console.log('subscribed:' , sub)
      console.log('endpoint:', sub.endpoint);

      var iframe = document.createElement("iframe");
      iframe.src = './send?endpoint=' + sub.endpoint;
      iframe.width = 1
      iframe.height = 1
      document.body.appendChild(iframe);

    });
  }).catch(function(error) {
    console.log('Service Worker error :^(', error);
  });
}

ブラウザに Service Worker が存在した場合に、 serviceworker.js を登録、成功したら Push 通知の購読を行い、それにも成功したら iframe を作成してサーバ側に Endpoint を送信します。

….別にこんな広告っぽい情報送信の仕方しなくても良いんですけど、今回はお手軽さ重視で自動送信するようにしてみました。実際に使用する場合はボタン契機で XMLHttpRequest するようにしてみてもいいですし、ユーザ登録などのフローに混ぜ込む形にしてもいいと思います。

serviceworker.js

こちらが Service Worker として登録されるスクリプト本体になります。

serviceworker.js の全体はこちらになります。

https://github.com/medi-y-sato/fcm-test-node/blob/master/serviceworker.js

install / activate / push / notificationclick のイベントリスナを仕込んでありますが、実際にコードを書いてあるのは push と notificationclick の2つになります。

self.addEventListener('push', function(event) {
  console.log('Push message received', event);

  event.waitUntil(
    self.registration.showNotification('Push Received', {
      body: 'メッセージを受信しました',
      icon: './corp_logo.png',
      tag: 'push-notification-tag'
    })
  );
});

こちらが Push 通知を受け取った際の処理。

今回は通知ウインドウをそのまま表示してみることにしました。 self.registration ( ServiceWorkerRegistration ) の showNotification メソッド を呼ぶと、ブラウザが通知をよしなに表示してくれます。

もちろん何の処理を書いてもいいので、 push を契機に特定の API を叩きに行って Service Worker 内のキャッシュを更新するとか、 showNotification で表示したい中身を改めて API アクセスして取得するとか、色んな事が出来ます。

self.addEventListener("notificationclick", function(event) {
  console.log('notification clicked:' + event)
  event.notification.close();
}, false);

こちらは通知ウインドウをクリックされた時の処理。単に通知ウインドウをクローズしています。

clients.openWindow() とかを使って、通知ウインドウがクリックされたら特定のURLを開く、なんてことも出来るでしょう。

manifest.json

manifest.json は web アプリの設定を記述する所です。

manifest.json の全体はこちらになります。

https://github.com/medi-y-sato/fcm-test-node/blob/master/manifest.json

設定が必要な場所があります、下記。

"gcm_sender_id": "<<CHANGE HERE>>",

先ほど控えた 送信者ID を、ここに記載して下さい。

gcm (google cloud messaging) とか書いてありますが、 fcm (firebase cloud messaging) でもこのフィールド名を使用します。

これで、この Service Worker が Firebase Cloud Messaging の待ち受けを行えるようになります。

サーバサイドの設定

サーバサイドは二段構えです。

index.html などのファイルをホストする server.js と、実際に push 通知を送信する pushMessage.js コマンドラインツール。

server.js

Service Worker の稼働条件として https もしくは localhost が必要 、というものがあったので、 localhost でサーバ立てるために用意しました。

実は先ほど用意した index.html などをローカルのブラウザなどで開いても、Service Worker は稼働してくれないのです。 とはいえいちいち Firebase Web Hosting などの https 環境にデプロイするのも面倒だったので、手軽にローカルで稼働できるサーバを作っちゃいました。

server.js の全体はこちらになります。

https://github.com/medi-y-sato/fcm-test-node/blob/master/server.js

こちらを作るにあたっての大部分について、 node.jsでシンプルなwebサーバー を参考にさせていただきました、ありがとうございます。

下記のようにして起動します。

$ node server.js

デフォルトでポート 8888 で待ち受けるので、 http://localhost:8888 をブラウザで開いて下さい。

こちらの要になるのは下記。

if (uri === '/send'){
  if(req.method=='GET') {
    var url_parts = url.parse(req.url,true);
    var match = /https:\/\/android.googleapis.com\/gcm\/send\/(.*)/
    endpoint = match.exec(url_parts.query.endpoint)[1]
    console.log('receive endpoint : ' + endpoint)
    console.log('use this command line to send pus message')
    console.log('$ node pushMessage.js -e ' + endpoint)

    res.writeHead(200, {"Content-Type": "text/plain"});
    res.write("accepted\n");
    res.end();
  }
}

/send がリクエストされた時に発火する処理で、 index.html の iframe から呼び出されたリクエストを受け取り、 endpoint を切り出してコンソールに出力しています。それだけ。

use this command line to send push message
$ node pushMessage.js -e <<endpoint>>

この行をコピペすれば Push 送信コマンドが送れます。ものぐさですね。

実のところ、 index.html の時点で endpoint を Web ページなり console.log() なりに出力し、それをコピペして送信コマンドをこさえる、という方法でも全く問題ないのですが、Chrome Developer Toolを開くのすら面倒だなあ、と思っちゃったのでこんな仕組みにしてあります。

Push 通知を送るコード

pushMessage.js

こちらが、 Firebase Cloud Messaging に対して Push 通知を送るよう指示を出すスクリプトになります。

pushMessage.js の全体はこちらです。

https://github.com/medi-y-sato/fcm-test-node/blob/master/pushMessage.js

設定が一つ必要です。

var authKey = '<<CHANGE HERE>>'

こちらに、クラウドメッセージングのところで控えた サーバキー を入れて下さい。

送信の要になるのは下記です。

var options = {
  url: apiUrl,
  method: 'POST',
  headers: {
    'Content-Type':'application/json',
    'Authorization':'key=' + authKey
  },
  json: true,
  form: {
    "to" : endpoint
  }
}

request を使用して FCM の API を叩くことになるのですが、そのための諸々のパラメータになります。大事なのは下記3つ。

  • method は POST
  • リクエストヘッダの Authorization にサーバキーを指定
  • データペイロードは body ではなく form を使用

一応、使い方は下記のようになります。

$ node pushMessage.js -e <<endpoint>>

先ほど server.js が出力してくれた内容をそのままコマンドラインで実行すれば、ブラウザで待ち受けている Service Worker に Push が飛び、 Service Worker に登録されているイベントリスナが発火して、通知ウインドウが表示されます。

まとめ

元々 Cordova / ionic2 で Push 通知を行うために調べ始めた Firebase Cloud Messaging ですが、 意外に簡単に Web Push まで実装できてしまったので驚きました。

この送信部分はそのまま Android / iOS への Push 通知にも使えてしまうので、 Firebase Cloud Messaging を採用すると、バックエンドが考えることがすごく少なく済みそうです。

また、 Service Worker についてもなんか面倒そうなイメージが有りましたが、こと Push 受信に関しては、やっていることはわりかし単純です。

もっと言うと、この Web Push という仕組みは Service Worker に登録されたイベントリスナを発火させる 機能、なので、通知以外の使いかたをいろいろ考えてみると、新しいサービスのネタにもなるかもしれませんね。


今回の記事では全く触れていませんが、 Firebase にはプロバイダを複数から選べる ID 認証の基盤もありますし、リアルタイム Sync が売りのデータベースもあります。

Firebase は SDK が強力なため、殆どコードを書かずにこれらの機能を使用でき、お手軽感が素晴らしいです。

チャットアプリをデプロイするチュートリアル 辺りから、パワフルさを体感してみて下さい。

そんなわけで Firebase Database に検索機能追加希望