這篇文章將完成把 Android 裝置註冊到 Azure 通知中樞的程式碼。
前置準備 在這篇文章中,你需要事先準備
前導知識 什麼是 APNS 與 FCM?
APNs(Apple Push Notification Service) :Apple 提供的推播服務,用來將通知送到 Apple 裝置,需先申請 Apple 開發者帳號才能取得。
FCM(Firebase Cloud Messaging) :Google 提供的推播服務,使用 Firebase SDK 取得。FCM 提供兩種 API:
Legacy API:舊版 API,用 server key 傳送請求,較簡單但功能有限,已經在 2024/06 被淘汰。
FCM v1(HTTP v1 API):新版 API,使用 OAuth 2.0 認證,支援多平台設定與更高安全性,Google 推薦使用。
Azure 推播通知運作方式 上圖來自 Azure 文件,我們可以從中了解 Azure 推播的運作流程。
裝置向 APNS(Apple)或 FCM(Google)索取推播識別碼,這將作為接收 Azure 的通道。
Azure 使用識別碼,註冊到 Azure 上。
後端把通知送到通知中樞。
通知中樞轉給平台 APNS(Apple)或 FCM(Google),由平台推送到裝置。
使用 REST API 註冊 Azure 其實整段程式碼很簡單,但可以使用 REST API 執行的教學藏在 C# 範例裡,我也是先找到很多提供整合的老舊套件,才發現原來 Azure 本身就有提供註冊方法。
1. 取得推播識別碼 安裝 react-native-firebase/messaging,根據 文件中 messaging 提供的 getToken 方法 取得 FCMtoken
。
1 npm i @react-native-firebase/messaging
通常裝置每次取得的 FCMtoken
是相同的,但在以下情境時會改變:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { useEffect } from 'react' ;import messaging from '@react-native-firebase/messaging' ;export default function HomeScreen ( ) { useEffect (() => { const setupPush = async ( ) => { try { const token = await messaging ().getToken (); console .log (`FCM token: ${token} ` ); } catch (err) { console .log (`初始化推播錯誤 ${JSON .stringify(err)} ` ) } } setupPush (); }, []); return (...略) }
2. 準備註冊需要的參數 我們會使用 建立 Azure 註冊 create-registration 這支 API。還記得我們在上一篇文章中有設定 Azure 通知中樞 FCM(v1)金鑰嗎?這支 API 會將剛才產生的 FCM token 註冊到其中。成功註冊的裝置會得到 registerId
等資訊,這未來能用於修改和刪除,建議存到後端。
這是我們註冊需要準備的資料:
namespace
:Notification Hub 所屬的命名空間(Namespace)
NotificationHub
:實際 Notification Hub 名稱,是你註冊裝置與推播的實際目標
SAStoken
:Shared Access Signature
namespace
和 NotificationHub
已經在上一篇文章建立通知中樞時設定好了,本文裡兩個 value 都用 AnnNotify
。
SAS token SAStoken
將由以下參數組成,程式碼在 官方文件 有附。
targetUri
:https://${HUB_NAMESPACE}.servicebus.windows.net/${HUB_NAME}
。
sharedKey
:’Access Policies 中 DefaultListenSharedAccessSignature 的金鑰值’。
ruleId
:’DefaultListenSharedAccessSignature’。因為共用存取簽章安全性 考量,建議 FullSharedAccess
只在後端使用,前端不要用呦。
expiresInMins
:過期時間(產生後幾分鐘失效),SAStoken
失效不影響已經成功的 Azure 註冊。其實建議設定短時間,並且由後端產生較安全,但前端還是可以自己先產出測試。
我們可以新增一個 hook 專門用來放註冊程式碼,直接從 官方文件 中複製貼上吧!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import axios from 'axios' import CryptoJS from 'crypto-js' const HUB_NAME = '通知中樞名稱' const HUB_NAMESPACE = '通知中樞命名空間' const SHARED_ACCESS_KEY = 'Access Policies 中 DefaultListenSharedAccessSignature 的金鑰值' const SHARED_ACCESS_KEY_NAME = 'DefaultListenSharedAccessSignature' const registerDeviceToAzure = async (fcmToken : string ): Promise <void > => { const baseUri = `https://${HUB_NAMESPACE} .servicebus.windows.net/${HUB_NAME} ` const apiVersion = '2015-01' const targetUri = `${baseUri} ` const sasToken = getSelfSignedToken ( targetUri, SHARED_ACCESS_KEY , SHARED_ACCESS_KEY_NAME , 30 ) } const getSelfSignedToken = function ( targetUri : string , sharedKey : string , ruleId : string , expiresInMins : number ) { targetUri = encodeURIComponent (targetUri.toLowerCase ()).toLowerCase () var expireOnDate = new Date () expireOnDate.setMinutes (expireOnDate.getMinutes () + expiresInMins) var expires = Date .UTC ( expireOnDate.getUTCFullYear (), expireOnDate.getUTCMonth (), expireOnDate.getUTCDate (), expireOnDate.getUTCHours (), expireOnDate.getUTCMinutes (), expireOnDate.getUTCSeconds () ) / 1000 var tosign = targetUri + '\n' + expires var signature = CryptoJS .HmacSHA256 (tosign, sharedKey) var base64signature = signature.toString (CryptoJS .enc .Base64 ) var base64UriEncoded = encodeURIComponent (base64signature) var token = 'SharedAccessSignature sr=' + targetUri + '&sig=' + base64UriEncoded + '&se=' + expires + '&skn=' + ruleId return token } export default registerDeviceToAzure
3. 註冊 Azure 通知中樞 回到 建立 Azure 註冊 create-registration 這支 API,不同系統裝置有不同註冊範本。這篇文章以 Android 為主,使用 Firebase,所以我的註冊範本 xmlBody
使用 FcmV1TemplateRegistration
的範例,其他系統可以再替換。 另外可以選擇使用以下標籤
Tags
:推播時可針對標籤推播,如果要設定只推給 Android 用戶,可使用 <Tags>Android</Tags>
。想推給某特定用戶,也可以自訂每個裝置有不同 Id,使用 Id 註冊。
ExpirationTime
:註冊有效時間。Azure 通知中樞的付費方案與可註冊數量有關,尤其是在開發者測試期間會反覆重新安裝(本文第 1 個步驟有提到 FCMtoken
更新機制),如果不設定有效時間,會累積大量新註冊。雖然有 刪除註冊 API ,但一次只能刪一個註冊。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 const registerDeviceToAzure = async (fcmToken : string ): Promise <void > => { const baseUri = `https://${HUB_NAMESPACE} .servicebus.windows.net/${HUB_NAME} ` ; const apiVersion = '2015-01' ; const targetUri = `${baseUri} ` ; const sasToken = getSelfSignedToken (targetUri, SHARED_ACCESS_KEY , SHARED_ACCESS_KEY_NAME , 30 ); const expirationDate = new Date (); expirationDate.setDate (expirationDate.getDate () + 30 ); const isoString = expirationDate.toISOString ().replace (/\.\d{3}Z$/ , '+08:00' ); const xmlBody = ` <entry xmlns="http://www.w3.org/2005/Atom"> <content type="application/xml"> <FcmV1RegistrationDescription xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect"> <Tags>...</Tags> <FcmV1RegistrationId>${fcmToken} </FcmV1RegistrationId> <ExpirationTime>${isoString} </ExpirationTime> </FcmV1RegistrationDescription> </content> </entry> ` ; try { const response = await axios.post (`${baseUri} /registrations?api-version=${apiVersion} ` , xmlBody, { headers : { 'Authorization' : sasToken, 'Content-Type' : 'application/atom+xml' } }); console .log ('📡 Azure 註冊成功:' , { status : response.status , registrationId : response.data ?.match (/<title[^>]*>(.*?)<\/title>/ )?.[1 ] ?? '不明' , }); } catch (error : any ) { console .error ('❌ Azure 註冊失敗:' , error); return `註冊失敗:${error.message} ` ; } }; const getSelfSignedToken = function (targetUri : string , sharedKey : string , ruleId : string , expiresInMins : number ) { ...略 }; export default registerDeviceToAzure;
4. 從頁面執行註冊 Hook 我們只剩一步就完成了! 回到要執行註冊動作的頁面,在程式碼下方把 Hook 方法加入。
1 2 3 4 const token = await messaging ().getToken ()console .log (`FCM token: ${token} ` )await registerDeviceToAzure (token)
完工!🎉🎉
接下來幾篇會介紹
要求裝置開啟通知授權
打包與 Android Studio 偵錯
測試發送