使用 Firebase 快速建立網頁推播服務 Web Push Notifications Service

本篇將專注於如何使用 FCM 提供的 SDK 來實作一個推播通知應用,當中會粗略帶到 Push API、Notification API、Push Server、Service Worker 這些背後的機制

Nick
16 min readAug 16, 2020
今天沒有要介紹 PushAlert 這個產品,只是他們圖很美又可以分享

此篇文章你將了解

  1. 從零使用 FCM SDK 配置一個可收 Web 推播的網站
  2. 介紹 Push API 機制跟串接 FCM SDK 在機制上的差異
  3. 文末有徵才訊息,前後端都有缺,歡迎投遞 > <

緣起,為何開始研究 Web Push Notifications

從工作一兩年以來就一直知道推播通知這個服務,也曾經寫過無數排程去針對不同對象發送不同推播訊息,藉此讓使用者們更多回訪產品來刷新生命週期,而這次不一樣的是遇到工作上收到一個推播失敗的錯誤,才比較深入研究發現 Push API 機制背後藏著蠻多學問的,本篇就想藉由從建立一個系統來跟大家分享其中的機制跟使用到的 Web Api 們,本篇預計閱讀 5 分鐘,實際實作 60 分鐘內可以完成。

在開發者社團發問 Web Push 的 issue 截圖

Web Push Notification 能做什麼

引用 airship 產品的介紹網頁推播通知的重點

  • 讓 Server 可主動推送 Push Message 給 Client 的服務
  • 快速,使用者只開啟網頁就能授權
  • 可以放到瀏覽器背景執行,讓使用者開著電腦就即時收到推播訊息
  • 推播伺服器可暫存訊息,等使用者重新開電腦或連線時會補收到訊息
圖片引用自 google web developer

Web Push Notification 背後的機制跟知識

由兩個 Web API 組成的,分別是

  • Push API:處理「訂閱授權及主動傳遞訊息」的機制
  • Notification API:處理「呈現推播」的行為

介紹 Push API 的機制:

推播機制圖,一開始用文字畫的很開心,結果忘記還有 mobile 使用者,最後只能改成截圖 > <

名詞定義

  • UA:User Agent,可比喻為「使用者」,實際為「網頁瀏覽器、前端」
  • Push Service:UA 對應的瀏覽器開發方實作的「推播伺服器」
  • Application:應用程式伺服器,這才是自家的「網頁後端、網頁伺服器」

流程

  1. 使用者向 Push Service (Server) 訂閱推播通知
  2. 使用者訂閱成功後就會與 Push Service 在背景保持一個長連線,並會得到一組「訂閱授權資訊」
  3. 使用者透過自家實作方式如 Restful API 將「註冊授權資訊」傳給 Application Server (網頁後端)。
  4. 網頁後端保存「授權資訊」到資料庫,在想發送訊息時把「授權資料」包含「想推播的訊息」一起傳給「Push Service (Server)」
  5. Push Service 接收到有推播訊息需求後會驗證授權,再把訊息推給「使用者」
小哉問時間:Q:如何在網頁未開的時候也能收到推播訊息?
A:一般來說網頁關掉邏輯就中斷了,但如果使用 Service Worker 這個 Web API 機制可以讓一段邏輯運轉在瀏覽器的背景,Web API 有提供可以讓背景程式介接 Notificaiton API 的方式讓訊息可以送到使用者畫面上,以達到網頁未開也能收到推播的功能。
Q:使用者如果裝置沒開或為網路,是怎麼暫存推播訊息的?
A:這是 Push Service 實作的機制,如果發現當前是與使用者(UA)沒有連線的,Service 就會先保留推播訊息直到使用者再連現在再補傳給他。 ---

小補充,各家瀏覽器會自己實作自己的 Push Service

Chrome回傳的推播註冊資訊,當中的 endpoint 為 fcm.googleapis.com 這個 domain
Firefox 回傳的推播註冊資訊,當中的 endpoint 為 updates.push.services.mozilla.com 這個 domain

介紹 Notification API:

概述完 Push API 的機制,我們來介紹 Notification Api,這個 Web Api 可以用來「呈現推播」的功能,以下大概是一個小範例。

只要使用者授權過可以發通知,可以直接在 devtool 裡透過這個 API 產生通知到使用者畫面上
不同參數可以產生不同通知樣子,比如這是一個不會自動消失的通知

來釐清一個細節觀念,一般我們看到的「網頁前端」跟「使用者」要取通知權限的畫面,其實指的是 Notification Api 用來「呈現通知」這個權限,而不是「Web Push 訂閱推播通知」機制的權限。

只是「Web Push 訂閱推播通知」機制觸發時會需要檢查此時是否已經擁有 Notification Api 的權限要求,沒有的話會直接觸發要求 Notification API 的權限,但只要授權一次,後續要重新訂閱推播通知都是可以直接在背景執行的。

chrome 要取授權的畫面
Firefox 要取授權的畫面
小哉問時間:Q:可能發生有註冊推播,卻沒有「呈現推播」權限的狀況嗎?
A:有,一開始應該是有授權,但可能後續「呈現推播」的權限被使用者關掉或誤關掉了,此時為了要讓後端知道此使用者已經取消授權,可以綁定一個取消授權時通知網頁後端更新使用者狀態的機制。
Q:可能發生沒有註冊推播,卻有「呈現推播」權限的狀況嗎?
A:有,因為 Notification API 是獨立的 Web Api,也可以單獨用來網頁的服務上,所以可能單獨被授權在網頁前端使用,不一定要綁定推播服務。

小知識補充:

Notification API 的授權 popup 是沒辦法客製化的,且當被拒絕後就沒辦法再次主動詢問授權,所以很多頁面會先做一個客製的 Dialog 跟使用者確認是否真的是想訂閱,再跳授權 popup,或跳 popup 時補顯示一個小提示減少使用者的突兀感。

Pinkoi 網頁上詢問使用者是否授權 Notification API 的畫面

四步驟來串接一個 Web Push Notification 服務

先讓專案動起來,再來講解程式碼吧

1. 建立一個 firebase project

https://console.firebase.google.com/

2.Fork 一份 Firebase 官方範例 Code

https://github.com/firebase/quickstart-js
git clone https://github.com/<your-account>/quickstart-js.git
cd quickstart-js/messaging/

3. 配置專案基礎設定

取得需配置的專案資訊

回剛建好的專案頁
點選「雲端通訊」或稱為「Cloud Messaging」
產生一組推播金鑰(此為公鑰)

回專案配置

  • 配置金鑰在 index.hmtl 的 89 行 messaging.usePublicVapidKey 的部分
配置註冊訂閱推播需要的公鑰

4. 引入 Firebase Cloud Messaging SDK

在 Firebase 新增一個 Cloud Messaging 的應用程式
先不用新增代管功能
取得一組 Firebase SDK的初始化程式

載入 Firebase SDK 並初始化來註冊我們專案的使用權,如以下把原本專案 index.html 裡 75 行部分並覆蓋掉。

覆蓋掉原本配置的部分

貼完後還需要再微調一下,因為剛剛的 SDK 初始化還不夠完整,當中並沒有載到我們重點要使用的 Firebase Cloud Messaging 的 SDK,所以透過以下文件找到後再補上。

以上次文件中各種 firebase SDK 的引入 CDN 網址,可以直接複製以下連結
<script src="https://www.gstatic.com/firebasejs/7.18.0/firebase-messaging.js"></script>
可以把 firebase.analytics 的 SDK 拿掉,取代為 firebase-messaging 的 SDK

除了 index.html 要初始化 Firebase SDK,還有一個地方是 service-worker 的檔案也需要配置,就把剛配置的部分、再稍微改一下語法就可以囉,如以下

檔案名為:firebase-messaging-sw

測試 Web Push Notification 機制

測試之前需要建立一個 web service,並能綁定 public 目錄在根目錄

推薦可以使用以下 chrome extension 小工具:Web Server for Chrome

https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb/related

安裝後直接點選啟動,就可以選擇我們目前專案資料夾為路徑,設定好後 web service 的根目錄就會是我們專案的目錄了。

這樣就可以讓 http://127.0.0.1:8887 的目錄對應到 /messaging 我們的專案資料夾

打開 http://127.0.0.1:8887 可以看到專案頁面,第一次打開應該會收到通知權限的授權需求。

第一次開啟畫面,則會收到授權通知的需求
完成授權後會得到一組 Firebase Cloud Messaging 的 Token
小提醒:如果不小心按到封鎖,所以點擊網址左邊的 icon,可以改為可以重新詢問授權,再重載頁面即可重新測試。

2. 測試 Web Push Notification 是否有訂閱成功

讓我們來利用 firebase 後台發送一個推播

點選 Send your first message

將一些測試訊息填入

填寫測試訊息後,點選傳送測試訊息
貼上註冊的 Token,點擊測試

此時應該會有兩種結果

  1. 一種是有收到通知
  2. 一種是沒有收到通知

廢話,但是沒有收到通知不用意外,因為之前有提到過「推送通知」跟「呈現通知」可以是兩個不同的步驟,這時可以先回到測試頁面上,應該會看到有呈現收到的通知訊息,主要是因為範例程式把接收的訊息改成用印在畫面上的方式去 handle,來讓使用者知道接收的訊息格式。

以上畫面就是有成功推送通知的情境
如果畫面沒訊息的話,或從前面配置看一下有沒有步驟遺漏掉了,或可以打開 devtool 看一下有無錯誤訊息,找不到問題歡迎在文末留言跟我說,記得多描述遇到的狀況 > <

如果想看推播被呈現成一個 Notification,可以用以下兩個方式,擇一嘗試

  1. 直接移除客製化 handle 推播訊息的程式,預設就是用 Notification API 呈現。
  2. 在客製化 handle 的位置再加觸發 Notification 的,會在 index.html 的 onMessage 這個部分。
在 index.html 中移除客製化處理推播訊息的邏輯
在 index.html 中客製化處理推播訊息的邏輯,接上 Notification API

Reload 測試頁面後,再到後台用 token 重發一次試試看

成功出現囉

Firebase Cloud Messaging 實作 Web Push Notification 的程式碼細節

  1. 配置 Firebase SDK
  2. 使用 getToken 取得訂閱推播的 Token
  3. 串接瀏覽器的 service-worker 接收到推播後交由 Firebase SDK 處理

配置 Firebase SDK,建立 instance

目前配置 Firebase SDK 都需要的第一步
建立完 firebase 的 instance 後,就可以再配置 Firebase messaging 的服務

使用 getToken 取得訂閱推播的 Token

配置在商業邏輯上想讓使用者訂閱推播的時機,也可以先 show 自製的 Dialog 再執行 getToken 來減少使用者點擊拒絕的結果。

而當執行 getToken 此會回傳一個 Promise 物件來非同步回傳訂閱推播的結果,成功時記得要把這個 Token 傳給自家的後端,以利後續發送推播。

串接瀏覽器的 service-worker 接收到推播後交由 Firebase SDK 處理

Q:為什麼在沒開啟網站的情況下還能收到推播?A:因為網頁前端有一個可以在背景執行的服務叫做 Service Worker,可以在進入一個網站後註冊到瀏覽器背景持續執行,而前面有講到的 Push API 就是在 Service Worker 裡面提供的介面。。

所以當我們執行 message.getToken 的同時,FCM SDK 已經幫我們去註冊 firebase-messaging-sw.js 這個 service worker 腳本在使用者瀏覽器上,在這個 service worker 中有實作監聽來自 Push Server 的推播訊息,再把訊息串接 Notification 呈現到使用者畫面上了。

devtool 的 Application 可以看到目前瀏覽器有註冊此 domain 下得 firebase-messaging-sw.js

你知道使用 FCM SDK 的同時,也讓所有使用者的訂閱權限授權了一份給 Google 嗎?

下了一個小聳動的小標題,但讓我來回顧一下文初說明 Push API 的過程

一般推播機制圖

實際上使用者與 Push Server 訂閱推播資訊會像是以下截圖,是由多種不同的資訊所組成,後端理論上需要把這全部資訊才能去授權才能發送一個推播給使用者。

那為什麼串接 FCM SDK 後只需要一組 Token 呢

一個推播訂閱的資訊包含 applicationPubKey、auth、endpoint、p256dh
使用 FCM SDK 後,只得到一組 Token,後端也只要用這組 Token 就能發送推播

答案揭曉,因為當我們使用 FCM SDK 時,是透過 Firebase 再去當 Application Server,來讓 Firebase 去跟 Push Server 發送推播請求,所以實際上還是需要對 Push Server 提供完整的推播資訊來發推播的,只是變成不是我們自己做,整體的機制會變成以下

串接 FCM API 後的推播機制圖

小結語跟探討

網路上其實蠻多 Firebase Cloud Messaging 的教學圖文,寫的都比我好上一百倍,但因為我起初遇到的問題不透過 FCM SDK 實作推播系統遇到的串接細節,想透過最多人使用的 FCM SDK 來研究自家服務缺少的內容。

實作過一輪後發現 FCM SDK 太驚人了,它幾乎把所有的細節全部都處理掉了,串起來跟原生串 Web Push 服務幾乎是完全不同的機制,也讓我思考「站在巨人肩膀上看世界」、「不要重複造輪子」的背後讓多少新起的開發者忽視到了許多實作的本質,所以激起這篇想透過一邊介紹 FCM SDK 的過程補充一些我所瞭解到的事,也期許自己有天能不靠 FCM SDK 實作過一次 Web Push,更深刻的比較過 FCM SDK 做的每一件事情背後邏輯後再初一篇文章來探討。

徵工程師,Pinkoi 跨境設計電商!

前端工程師點這後端工程師點這其他職缺點這

參考資料

w3 push-api docs

Web Push Notifications: Timely, Relevant, and Precise

Message Encryption for Web Push

web-push-libs

firebase Set up a JavaScript Firebase Cloud Messaging client app

如果喜歡我這篇文,可以幫我拍手 1-10 下
如果覺得這文章對你有幫助,可以幫我拍手 10–30
如果對於原生實作 Web Push 機制或拆解 FCM SDK 實作背後邏輯感興趣,可以幫我拍手 30–50
也記得 Follow 我 陳建宇 才不會錯過新文章哦
歡迎在下方留言,我很樂意與你討論聊天或回答問題!

--

--

Nick

嗨 我是 Nick,目前在 ShopBack 擔任軟體工程師,喜歡用科技解決問題,偶爾會整理一些工作上的發現分享在這,歡迎大家追蹤及交流