網頁訊息推送
瀏覽器允許業務服務向用戶客戶端推送訊息,客戶端收到推送訊息後以通知的形式展示出來。業務運營人員可以通過這項功能定向給使用者推送推薦訊息或者重要通知,用於提升使用者留存和使用者使用時長。
下文demo全部程式碼可以在news-push-example中看到。
簡述
訊息推送效果如下(當用戶沒有開啟業務網站時,業務網站也能正常通知,在最終效果中有展示):
網頁訊息推送流程圖如下所示:
整個過程主要可以分成訂閱和推送這兩部分,參與的角色主要有瀏覽器、瀏覽器推送服務、業務服務,下圖可以看到這三者之間的關係(圖片出自Web Push草案)
+-------+ +--------------+ +-------------+ | UA | | Push Service | | Application | +-------+ +--------------+ | Server | | | +-------------+ | Subscribe | | |--------------------->| | | Monitor | | |<====================>| | | | | | Distribute Push Resource | |-------------------------------------------->| | | | : : : | | Push Message(1) | | Push Message(2) |<---------------------| |<---------------------| | | | |
注:
UA:瀏覽器客戶端
Push Service:瀏覽器推送服務。不同瀏覽器廠商會部署自己的推送服務。
Application Server:業務服務。主要用於存放使用者客戶端訂閱資訊和向push service推送訊息兩部分工作。
訂閱流程:
- Subscribe:瀏覽器向push service發起推送訂閱,獲得訂閱資訊。
- Monitor:push service存放訂閱資訊,後面會使用訂閱資訊向特定瀏覽器下發通知。
- Distribute Push Resource:瀏覽器將從push service獲取到的訂閱資訊發給業務服務,業務服務將訂閱資訊和使用者資訊繫結存放起來。
推送流程:
- Push Message(1):業務服務使用Web推送協議將通知內容推送給push service。
- Push Message(2):push servicee將通知內容下發給使用者瀏覽器。
訂閱
訂閱主要任務如下圖所示(圖片來自How JavaScript works: the mechanics of Web Push Notifications)
- 網頁獲取通知授權
- 獲得在推送服務中的訂閱資訊
- 將訂閱資訊存放到業務服務中
網頁獲取通知授權
通過呼叫Notification.requestPermission()介面來拉起授權申請,授權申請的效果如下所示(每個瀏覽器效果都不太一樣,這裡主要展示chrome的效果):
程式碼展示如下:
Notification.requestPermission()
介面執行會返回授權結果,主要有granted
(已授權)、denied
(被拒絕)、default
(被關閉)這3中狀態。只有當授權結果為granted
時,當前網頁才能進行後面訂閱推送服務和通知訊息這兩個步驟。
注意:只有在使用者沒有授權或者拒絕的情況下,瀏覽器才會拉起授權面板。
註冊service work、訂閱推送服務
通過呼叫service work(後面都統一叫sw)註冊物件上的訂閱api來實現訂閱推送服務,因此註冊sw任務需要在訂閱推送服務前執行。
註冊service work
通過呼叫navigator.serviceWorker.register()介面來註冊sw,程式碼展示如下:
navigator.serviceWorker.register()
呼叫時,需要傳入一個執行在服務工作執行緒(service work)的js檔案。
呼叫navigator.serviceWorker.register()會返回一個promise物件,我們可以在then鏈路中獲得sw註冊物件(registration)。
訂閱推送服務
通過呼叫registration.pushManager.subscribe()獲取訂閱資訊。
在開始訂閱推送服務前,我們需要確認以下兩個任務是否完成:
- 使用者已經授權網頁通知
- sw註冊成功
1和2兩個條件之間沒有任何限制,這裡使用Promise.all
來讓任務1和2並行,提升一點效率,程式碼展示如下:
registration.pushManager.subscribe()
呼叫時需要傳入了userVisibleOnly
和applicationServerKey
這兩個引數
- userVisibleOnly:瀏覽器是否以通知的形式展示push service下發的推送訊息,這裡必須傳true。
- applicationServerKey:包含base64編碼的公鑰(publickKey)的DOMString或者ArrayBuffer。公鑰(publickKey)可以在web-push-codelab網頁中獲取,也可以使用web-push npm工具快速生成,同時需要把私鑰也記下來,後面
push message
步驟需要用到。
registration.pushManager.subscribe()會返回一個promise物件,我們可以在then鏈路中獲得訂閱資訊,訂閱資訊中主要有endpoint
和keys
這兩部分內容:
- endpoint:推送服務連結,需要使用post方式請求該連結。
- keys:用於加密推送的訊息內容,防止訊息內容被三方竊取。
注意:谷歌瀏覽器推送服務是FCM服務(國內被牆),使用谷歌時,需要先翻牆,然後再呼叫registration.pushManager.subscribe api,這樣才能成功訂閱推送服務。
訂閱資訊存放
前端拿到訂閱資訊以後,將訂閱請求傳給業務後端,業務後端得到訂閱資訊以後,將訂閱資訊和使用者關聯起來,等到需要向用戶推送訊息的時候再提取該使用者對應的訂閱資訊。
推送
推送的主要流程如下圖所示(圖片來自How JavaScript works: the mechanics of Web Push Notifications):
- 業務服務向push service推送訊息
- push service向瀏覽器推送訊息
- 瀏覽器展示訊息
推送訊息
當業務方需要向用戶推送通知時,業務服務需要從資料庫中取出使用者的訂閱資訊,然後帶著訊息內容去請求訂閱資訊中的推送服務連結。請求推送服務時必須依據Web推送協議,node應用中可以使用web-push工具幫助我們快速實現請求推送服務功能,程式碼如下:
實現流程:
- 給
web-push
工具設定VAPID keys
。 - 讀取伺服器中存放的訂閱資訊。訂閱資訊中有
endpoint
、key
。 - 生成推送訊息內容
pushData
、生成web-push
配置options
資訊。- 推送訊息內容必須是一個沒有特殊字元的字串或者是一個buffer物件,如果想傳輸json資料,可以將json資料轉成buffer物件後再傳輸
- options中的
proxy
欄位是請求推送服務(谷歌、火狐的訊息推送服務是被牆了的)時設定的代理(如果業務服務部署在大陸以外,就可以不用設定代理),其他引數可以在web-push中看到詳解
- 呼叫
webPush.sendNotification()
向push service推送訊息
注意:由於谷歌和火狐訊息推送服務被牆了,我們可以在使用web-push工具推送訊息時設定proxy引數來進行請求代理,也可以將業務服務部署在大陸以外。
瀏覽器通知
push service根據業務服務推送過來的客戶端訂閱資訊,獲取到具體的使用者瀏覽器客戶端後,將訊息下發給瀏覽器,瀏覽器將訊息解密以後,會去觸發相應的service work的push事件,此時只要sw檔案中監聽push事件,就能夠獲取到業務服務端推送的訊息內容。監聽push程式碼展示如下:
service work中通過呼叫self.registration.showNotification()
函式拉起瀏覽器通知,程式碼中showNotification
函式呼叫傳參與通知UI的對應關係如下面兩張圖所示(更多引數可以在showNotification mdn中看到):
當用戶點選通知時,會觸發sw中的notificationclick
事件,同時,瀏覽器會將之前設定的action值一併傳入回撥函式,程式碼展示如下:
訊息推送效果
結語
國內谷歌使用者,如果沒有翻牆,瀏覽器客戶端將無法訂閱FCM服務以及無法收到FCM服務下發的訊息推送。業務服務在請求推送服務的時候,也需要設定設定代理,或者將業務服務部署到大陸外的網路。
國內火狐使用者,只要業務服務能夠正常請求瀏覽器推送服務,那麼使用者瀏覽器客戶端就能正常收到推送服務下發的訊息。
暫時測試國內的瀏覽器,但是據瞭解,國內的防毒軟體可能將瀏覽器通知當病毒來掃描,qq瀏覽器有開發推送服務但是好像沒有開放出來使用。