Firebase Cloud Messaging + VWG

Firebase Cloud MessagingWeb PushWeb APIService Worker 好多名詞,😂

Firebase Cloud Messaging (FCM) 前身係 Google Cloud Messaging (GCM),Google 買咗 Firebase 之後將 GCM 併入 Firebase 改名為 FCM,FCM 可以推送你嘅 notifications 去 iOS/Android devices 同埋 Web Browser。iOS 同 Android 收 notification 一啲都冇出奇,因為自從有 smart phone 就好似有 notification 哩樣嘢,我要玩嘅係:用 VWG (C#)喺 server 推送 notification 俾 desktop computers’ Web Browsers,好新鮮嘅技術,2015 年底先至有,目前好似淨係 Chrome 同 Firefox 有此功能,Safari 都未跟上。

Web Push Notification 係初期嘅叫法,之後就叫口咗 Web API,Web Browser 要識得支援 Service Worker (ref. Service Worker Cookbook) 先至可以收到 notification,個手續如下:

sequence_diagram

  • 你個用戶用 browser 去訪問你個網頁
  • 你嘅 web page 會睇睇隻 browser 支唔支持 service worker,如果 yes,就叫佢記低你寫嘅 service worker 代碼
  • 你再 check 吓佢有冇 subscribe,冇就跟手問哩個用戶係咪要 subscribe,佢一定要答 yes,因為你唔可以強迫佢 subscribe
  • 成功 subscribed 嘅話 FCM 就會送返幾個關於哩位用戶嘅重要資料:endpoint / user key / user auth
  • 你個 web page 要將哩幾個資料送返俾 server
  • server 之後就係根據哩啲資料送出 notification 俾哩位用戶

咩嘢係 subscribe?要收 notification 嘅話就要做哩個指定動作:訂閱,subscribe 係用戶自願嘅,佢亦都可以隨時退出。

好明顯,用戶雖然係訪問你個 web page,佢唔係直接同你隻 server 溝通,佢係同 FCM 溝通,而你隻 server 都係同 FCM 交涉,所以,多咗 FCM 做中間人,debug 起上嚟都幾麻煩,再加上要用大量 JavaScript,難上加難!😰

  • web page 我用這位網友嘅 code:GoogleChrome/Push-Notifications,第 08-unsubscribe。
  • Web Push 可以參考 web-push-lib / web-push,不過佢係用 JavaScript,而且要用到 node.js,IIS 跑 node.js 就要安裝 iisnode,我就唔想搞到咁複雜,試都冇試。
  • 另外,web-push-lib / webpush-java 有 java 版,C# 可以用 IKVM compile 之後可以直接喺你個 project 用,我試咗,IKVM 真係可以將啲 jar files compile 成 C# 可以直接使用的 DLL,不過我測試隻 webpush-java dll 嘅時候就有啲問題(出在 encryption),所以亦都放棄咗哩個辦法。😇
  • .NET 我試咗 PushSharp,好成功,不過就 send 唔倒 message,佢冇做 encryption 哩個部份,因為 custom message 一定要 encrypted,稱為 payload。
  • 最後,喺 StackOverFlow 有網友問 C# payload encryption 嘅問題,有高手答:可以用 WebPushHelper.cs,同時喺 web-push 嘅 issues 有相同的問題,原本連原作者都話未有辦法加 encrypted payload,不過終於亦有人答可以用同一個解決方法,咁我就照跟,試咗,真係得嘅!😀

 

capture_01 capture_02 capture_03
Push-Notification 的 08-unsubscribe,IDE 我用 WebStorm (OSX 10.11.6) Open in Browser (Chrome),我改咗少少,叫佢顯示 subscribe 之後嘅資料:endpoint / Registration Id / User Auth / User Key Click 咗 subscribe button,成功 subscribed。
capture_04 capture_05 capture_06
Server side 嘅 VWG source code: VWG.Firebase.WebPushTest project. Debug run. 然後 Copy/Paste:Device ID=endpoint, Message=隨意輸入, User Auth, User Key,然後 click Push (Payload) . web page 部電腦嘅右上角出現 Push Notification。

 

web page 改咗咗啲咩?一齊去睇睇:

<!DOCTYPE html>
<html>
<head>

  <title>Push Notification codelab</title>

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

</head>

<body>

  <h1>Push Notification codelab</h1>

  <p>This page must be accessed using HTTPS or via localhost.</p>

  <button disabled>Subscribe</button>

  <br />
  <h5>Endpoint URL</h5>

  <pre><code class="js-endpoint"></code></pre>

  <h5>Registration Id</h5>

  <pre><code class="js-registrationId"></code></pre>

  <h5>User Auth</h5>

  <pre><code class="js-userauth"></code></pre>

  <h5>User Key</h5>

  <pre><code class="js-userkey"></code></pre>

  <script src="js/main.js"></script>

</body>
</html>

 

Line 19~32 係我加嘅,攞幾個位嚟顯示啲 subscribed 資料,main.js 相應改咗:

function subscribe() {
  reg.pushManager.subscribe({userVisibleOnly: true}).
  then(function(pushSubscription) {
    sub = pushSubscription;

        const subObject = JSON.parse(JSON.stringify(pushSubscription));
    var endpoint =sub.endpoint;
    var endpointParts = endpoint.split('/');
    var registrationId = endpointParts[endpointParts.length - 1];

    console.log('Subscribed! Endpoint:', sub.endpoint);

    document.querySelector('.js-endpoint').textContent = endpoint;
    document.querySelector('.js-registrationId').textContent = registrationId;;
        document.querySelector('.js-userauth').textContent = subObject.keys.auth;
        document.querySelector('.js-userkey').textContent = subObject.keys.p256dh;;

    subscribeButton.textContent = 'Unsubscribe';
    isSubscribed = true;
  });
}

Line 6~16 係將啲 subscribed 資料抽出嚟放落對應嘅 html tag 顯示喺 browser。而,另外一個檔案 sw.js 就係 Service Worker cache 咗嘅 JavaScript codes:

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

  var obj = JSON.stringify(event.data);
  var payload = event.data ? event.data.text() : 'no payload';
  var data = JSON.parse(payload);

  var title = data.notification.title || 'Push message';
  var msg = data.notification.body || 'The Message';

  event.waitUntil(
    self.registration.showNotification(title, {
      'body': msg,
      'icon': 'images/icon.png'
    }));
});

我淨係改咗個 ‘push’ event,當 Service Worker 收到 push notification 嘅時候佢會拆解個 encrypted payload,如果個 payload 有料嘅話佢就抽出啲有用嘅 message,顯示一個 Push Notification。係咪好簡單?😀

再去睇 VWG 個 button click event:

private void cmdWebPushPayload_Click(object sender, EventArgs e)
{
    if ((txtDeviceId.Text.Trim() != String.Empty) && (txtMessage.Text.Trim() != String.Empty))
    {
        String fb_ApiKey = ConfigurationManager.AppSettings["Firebase_ApiKey"];

        String endpoint = txtDeviceId.Text.Trim();
        String message = txtMessage.Text.Trim();

        byte[] payload = Encoding.ASCII.GetBytes(message);

        String[] endpointParts = endpoint.Split('/');
        String deviceId = (endpointParts.Length > 1) ? endpointParts[endpointParts.Length - 1] : endpoint;

        var data = new
        {
            to = deviceId,
            notification = new
            {
                body = message,
                title = "Send via WebPushHelper",
                icon = "myIconUrl"
            },
            priority = "high"
        };
        var serializer = new JavaScriptSerializer();
        var json = serializer.Serialize(data);

        WebPushHelper.SendNotification(endpoint, json, txtUseKey.Text.Trim(), txtUserAuth.Text.Trim(), 0, 0, false);
    }
}

 

好易明啫,砌好啲資料然後喺 line 29 call WebPushHelper.cs 其中一個 function,WebPushHelper 就會將啲資料 encrypt 咗再送俾 FCM,FCM 接手,冇問題就送出去,跟住 user 個 browser 就會收到個 Push Notification。要提一提嘅就係,Firefox 個 endpoint URL 同 Chrome 係唔同嘅,所以你將成個 endpoint 完完整整咁交俾 WebPushHelper,佢會懂得分 Chrome 同 Firefox,所以同一個 source code 喺兩個 browsers 都 work!

謝謝!😀