共用方式為


教學課程:透過後端服務使用 Azure 通知中樞將推播通知傳送至 React Native 應用程式

下載範例 下載範例

在本教學課程中,您會使用 Azure 通知中樞將通知推播至以 AndroidiOS 為目標的 React Native 應用程式。

ASP.NET Core Web API 後端是用來使用最新且最佳的安裝方法來處理客戶端的裝置註冊。 此服務也會以跨平臺的方式傳送推播通知。

這些作業是使用 適用於後端作業的通知中樞 SDK 來處理。 如需整體方法的進一步詳細數據,請參閱 從您的應用程式後端註冊 檔。

本教學課程會引導您完成下列步驟:

Prerequisites

若要跟著做,您需要:

  • 您可以在其中建立和管理資源的 Azure 訂 用帳戶。
  • 已安裝 Visual Studio for Mac mac (或執行 Visual Studio 2019具有 .NET 行動裝置開發工作負載的電腦) 。
  • Android (實體或模擬器裝置上執行應用程式的能力,) 或 iOS (實體裝置) 。

針對 Android,您必須具備:

  • 開發人員已解除鎖定實體裝置或模擬器 , (執行 API 26 和更新版本,且已安裝 Google Play Services)

針對 iOS,您必須具備:

注意

iOS 模擬器不支援遠端通知,因此在 iOS 上探索此範例時需要實體裝置。 不過,您不需要在 AndroidiOS 上執行應用程式,才能完成本教學課程。

您可以遵循此第一個原則範例中的步驟,且沒有先前的經驗。 不過,您將受益於熟悉下列層面。

提供的步驟適用於 Visual Studio for MacVisual Studio Code,但可以使用 Visual Studio 2019 遵循。

設定推播通知服務和 Azure 通知中樞

在本節中,您會設定 Firebase 雲端通訊 (FCM) Apple Push Notification Services (APNS) 。 接著,您可以建立並設定通知中樞來使用這些服務。

建立 Firebase 專案並啟用 Android 的 Firebase 雲端通訊

  1. 登入 Firebase 控制台。 建立新的 Firebase 專案,輸入 PushDemo 作為 項目名稱

    注意

    系統將會為您產生唯一的名稱。 根據預設,這是由您提供之名稱的小寫變體所組成,加上以虛線分隔的產生數位。 如果您想要提供它仍然是全域唯一的,您可以變更此專案。

  2. 建立項目之後,請選取 [ 將 Firebase 新增至 Android 應用程式]。

    將 Firebase 新增至 Android 應用程式

  3. 在 [ 將 Firebase 新增至 Android 應用程式 ] 頁面上,執行下列步驟。

    1. 針對 Android套件名稱,輸入套件的名稱。 例如: com.<organization_identifier>.<package_name>

      指定套件名稱

    2. 選取 [註冊應用程式]。

    3. 選取 [下載google-services.json]。 然後將檔案儲存到本機資料夾,以供稍後使用,然後選取 [ 下一步]。

      下載google-services.json

    4. 選取 [下一步]。

    5. 選取 [繼續] 主控台

      注意

      如果未啟用 [ 繼續控制台 ] 按鈕,因為 確認安裝 檢查,請選擇 [略過此步驟]。

  4. 在 Firebase 控制台中,選取項目的齒輪。 然後選取 [項目設定]。

    選取項目設定

    注意

    如果您尚未下載 google-services.json 檔案,您可以在此頁面下載。

  5. 切換至頂端的 [ 雲端通訊] 索引 標籤。 複製並儲存 伺服器金鑰 以供稍後使用。 您可以使用此值來設定通知中樞。

    複製伺服器金鑰

註冊 iOS 應用程式以取得推播通知

若要將推播通知傳送至 iOS 應用程式,請向 Apple 註冊您的應用程式,以及註冊推播通知。

  1. 如果您尚未註冊您的應用程式,請流覽至 Apple 開發人員中心的 iOS 佈建入口網站 。 使用您的 Apple ID 登入入口網站,流覽至 [ 憑證]、[標識子 & 配置檔],然後選取 [ 標識符]。 按兩下 + 即可註冊新的應用程式。

    iOS 布建入口網站應用程式識別碼頁面

  2. 在 [ 註冊新的標識符 ] 畫面上,選取 [ 應用程式標識符] 單選按鈕。 然後選取 [繼續]。

    iOS 布建入口網站註冊新的標識碼頁面

  3. 更新新應用程式的下列三個值,然後選取 [ 繼續]:

    • 描述:輸入應用程式的描述性名稱。

    • 套件組合標識碼:輸入 com.organization_identifier<窗體的套件組合標識碼>。<如應用程式散發指南中所述product_name> 在下列螢幕快照中,此值 mobcat 會當做組織標識碼使用,並將 PushDemo 值當做產品名稱使用。

      iOS 布建入口網站註冊應用程式標識碼頁面

    • 推播通知:檢查 [功能] 區段中的 [推播通知] 選項。

      註冊新應用程式識別碼的表單

      此動作會產生您的應用程式識別碼,並要求您確認資訊。 選取 [繼續],然後選取 [ 註冊 ] 以確認新的應用程式標識符。

      確認新的應用程式識別碼

      選取 [ 註冊] 之後,您會在 [ 憑證]、[標識符] & [配置檔 ] 頁面中看到新的應用程式識別符作為明細專案。

  4. 在 [ 憑證]、[標識符 & 配置檔] 頁面的 [ 標識符] 下,找出您所建立的應用程式標識符明細專案。 然後,選取其數據列以顯示 [編輯您的應用程式識別符設定 ] 畫面。

建立通知中樞的憑證

必須有憑證,才能讓通知中樞使用 Apple Push Notification Services (APNS) ,而且可以使用下列兩種方式之一提供:

  1. 建立 p12 推播憑證,直接上傳至通知中樞 , (原始方法)

  2. 建立 p8 憑證,以用於令牌型驗證 , (較新的和建議方法)

較新的方法有一些優點,如 令牌型 (HTTP/2) APNS 驗證中所述。 需要較少的步驟,但也適用於特定案例。 不過,已針對這兩種方法提供步驟,因為任一方法都適用於本教學課程的目的。

選項 1:建立可直接上傳至通知中樞的 p12 推播憑證
  1. 在您的 Mac 上,執行 Keychain 存取工具。 它可以從 [公用程式 ] 資料夾或啟動列上的 [其他 ] 資料夾開啟。

  2. 選取 [金鑰鏈存取],展開 [ 憑證小幫手],然後 選取 [向證書頒發機構單位要求憑證]。

    使用 Keychain 存取來要求新的憑證

    注意

    根據預設,Keychain Access 會選取清單中的第一個專案。 如果您位於 [憑證 ] 類別,而 Apple 全球開發人員關係證書頒發機構單位 不是清單中的第一個專案,則這可能會是問題。 產生 CSR (憑證簽署要求) 之前,請確定您已選取非密鑰專案或 Apple 全球開發人員關係證書頒發機構單位 密鑰。

  3. 選取 [使用者 Email 位址],輸入您的 [一般名稱] 值,確定您指定 [已儲存到磁碟],然後選取 [繼續]。 將 CA Email 位址保留空白,因為不需要。

    預期的憑證資訊

  4. 在 [另存新檔] 中輸入憑證簽署要求 (CSR) 檔案的名稱,選取[位置] 中的位置,然後選取 [儲存]。

    選擇憑證的檔名

    此動作會將 CSR 檔案 儲存在選取的位置。 默認位置為 Desktop。 請記住為檔案選擇的位置。

  5. 回到iOS布建入口網站中的 [憑證]、[標識符 & 配置檔] 頁面,向下卷動至核取的 [推播通知] 選項,然後選取 [設定] 以建立憑證。

    編輯應用程式識別碼頁面

  6. [Apple 推播通知服務 TLS/SSL 憑證] 視窗隨即出現。 選取 [開發 TLS/SSL 憑證] 區段底下的 [建立憑證] 按鈕。

    [建立應用程式識別碼] 按鈕的憑證

    隨即顯示 [建立新的憑證 ] 畫面。

    注意

    本教學課程使用開發憑證。 註冊生產憑證時會使用相同的程式。 只要確定您在傳送通知時使用相同的憑證類型。

  7. 選取 [選擇檔案],流覽至您儲存 CSR 檔案的位置,然後按兩下憑證名稱以載入它。 然後選取 [繼續]。

  8. 在入口網站建立憑證之後,選取 [ 下載 ] 按鈕。 儲存憑證,並記住其儲存位置。

    產生的憑證下載頁面

    憑證會下載並儲存到 [ 下載 ] 資料夾中的電腦。

    在 [下載] 資料夾中找出憑證檔案

    注意

    根據預設,下載的開發憑證會命名 為 aps_development.cer

  9. 按兩下下載的推播憑證 aps_development.cer。 此動作會在 Keychain 中安裝新的憑證,如下圖所示:

    顯示新憑證的金鑰鏈存取憑證清單

    注意

    雖然憑證中的名稱可能不同,但名稱前面會加上 Apple Development iOS Push Services ,並具有與其相關聯的適當套件組合標識碼。

  10. 在 [金鑰鏈存取] 中,按下 + 您在 [憑證] 類別中建立的新推播憑證。 選取 [匯出],將檔案命名為 p12 格式,然後選取 [ 儲存]。

    將憑證匯出為 p12 格式

    您可以選擇使用密碼來保護憑證,但密碼是選擇性的。 如果您想要略過密碼建立,請按兩下 [ 確定 ]。 記下匯出 p12 憑證的檔名和位置。 它們用來啟用APN的驗證。

    注意

    您的 p12 檔名和位置可能與本教學課程中所說明的內容不同。

選項 2:建立可用於令牌型驗證的 p8 憑證
  1. 記下下列詳細資料:

    • 應用程式標識碼前置詞 (小組標識碼)
    • 套件組合標識碼
  2. 回到 [憑證],[標識符] & [配置檔],按兩下 [ 金鑰]。

    注意

    如果您已經為 APNS 設定金鑰,您可以重複使用您在建立 APNS 後立即下載的 p8 憑證。 如果是,您可以忽略步驟 35

  3. +按鍵 (或 [建立金鑰] 按鈕) 來建立新的金鑰。

  4. 提供適當的 [金鑰名稱] 值,然後核取 [Apple Push Notifications 服務] ([APNS) ] 選項,然後按兩下 [ 繼續],然後在下一個畫面上按兩下 [ 註冊 ]。

  5. 按兩下 [下載],然後將前面加上 AuthKey_) p8 (檔案移至安全本機目錄,然後按兩下 [完成]。

    注意

    請務必將 p8 檔案保留在安全的位置 (,並儲存備份) 。 下載金鑰之後,就無法在移除伺服器複本時重新下載。

  6. [金鑰] 上,如果您選擇改用) ,請按下您建立 (或現有金鑰的金鑰。

  7. 記下 金鑰識別碼 值。

  8. 在您選擇的適當應用程式中開啟 p8 憑證,例如 Visual Studio Code。 記下 -----BEGIN 私鑰----------END 私鑰 之間的金鑰 (值-----) 。

    -----BEGIN 私鑰-----
    <key_value>
    -----END 私鑰-----

    注意

    這是稍後將用來設定通知中樞令牌值

在這些步驟結束時,您應該會有下列資訊,以供稍後 使用APNS資訊設定通知中樞

  • 小組標識子 (請參閱步驟 1)
  • 套件組合標識碼 (請參閱步驟 1)
  • 密鑰標識碼 (請參閱步驟 7)
  • 在步驟 8) 中取得的令牌值 (p8 索引鍵值

建立應用程式的布建配置檔

  1. 返回 iOS 布建入口網站,選取 [ 憑證]、[標識符 & 配置檔]、從左側功能表中選取 [ 配置檔 ],然後選取 + 以建立新的配置檔。 [ 註冊新的布建配置檔 ] 畫面隨即出現。

  2. 選取 [開發] 下的 [iOS 應用程式開發] 作為布建配置檔類型,然後選取 [繼續]。

    布建配置檔清單

  3. 接下來,從 [ 應用程式 標識符] 下拉式清單中選取您建立的應用程式識別碼,然後選取 [ 繼續]。

    選取應用程式識別碼

  4. 在 [ 選取憑證 ] 視窗中,選取您用於程式代碼簽署的開發憑證,然後選取 [ 繼續]。

    注意

    此憑證不是您在 上一個步驟中建立的推送憑證。 這是您的開發憑證。 如果不存在,您必須建立它,因為這是本教學課程 的必要條件 。 開發人員憑證可以透過 XcodeVisual Studio,在 Apple Developer Portal 中建立。

  5. 返回 [ 憑證]、[標識符 & 配置檔 ] 頁面,從左側功能表中選取 [ 配置檔 ],然後選取 + 以建立新的配置檔。 [ 註冊新的布建配置檔 ] 畫面隨即出現。

  6. 在 [ 選取憑證 ] 視窗中,選取您建立的開發憑證。 然後選取 [繼續]。

  7. 接下來,選取要用於測試的裝置,然後選取 [ 繼續]。

  8. 最後,在 [ 布建配置檔名稱] 中選擇設定檔的名稱,然後選取 [ 產生]。

    選擇布建配置檔名稱

  9. 建立新的布建配置檔時,選取 [ 下載]。 請記住儲存的位置。

  10. 流覽至布建配置檔的位置,然後按兩下它以在開發電腦上安裝它。

建立通知中樞

在本節中,您會建立通知中樞,並使用 APNS設定驗證。 您可以使用 p12 推播憑證或令牌型驗證。 如果您想要使用已建立的通知中樞,您可以跳至步驟 5。

  1. 登入 Azure

  2. 按兩下 [建立資源],然後搜尋並選擇 [ 通知中樞],然後按兩下 [ 建立]。

  3. 更新下列欄位,然後按兩下列欄位[ 建立]:

    基本詳細數據

    訂閱: 從下拉式清單中選擇目標 用帳戶
    資源群組: 建立新的資源群組 (或挑選現有的 資源群組)

    命名空間詳細數據

    通知中樞命名空間: 輸入 通知中樞 命名空間的全域唯一名稱

    注意

    確定已為此欄位選取 [ 建立新 ] 選項。

    通知中樞詳細數據

    通知中樞:輸入通知中樞的名稱
    位置: 從下拉式清單中選擇適當的位置
    定價層: 保留預設的 [免費] 選項

    注意

    除非您已達到免費層上的中樞數目上限。

  4. 布建 通知中樞 之後,請流覽至該資源。

  5. 流覽至新的 通知中樞

  6. 從 [管理]) 底下 (列表中選取 [存取原則]。

  7. 記下 [ 原則名稱] 值及其對應的 連接字串 值。

使用APNS資訊設定通知中樞

[通知服務] 底下,選取 [Apple ],然後根據您先前在 [建立通知中樞憑證 ] 區段中所選擇的方法,遵循適當的步驟。

注意

只有在您想要將推播通知傳送給從市集購買應用程式的使用者時,才使用「 生產 」for Application Mode

選項 1:使用 .p12 推播憑證

  1. 選取 [憑證]。

  2. 選取檔案圖示。

  3. 選取您稍早導出的 .p12 檔案,然後選取 [ 開啟]。

  4. 如有必要,請指定正確的密碼。

  5. 選取 [沙盒 模式]。

  6. 選取 [儲存]。

選項 2:使用令牌型驗證

  1. 選取 [令牌]。

  2. 輸入您稍早取得的下列值:

    • 金鑰識別碼
    • 套件組合標識碼
    • 小組標識碼
    • 令牌
  3. 選擇 [沙盒]。

  4. 選取 [儲存]。

使用 FCM 資訊設定通知中樞

  1. 在左側功能表中的 [設定] 區段中,選取 [Google (GCM/FCM)
  2. 輸入您從Google Firebase 控制台向下注注的伺服器密鑰
  3. 選取工具列上的 [ 儲存 ]。

建立 ASP.NET Core Web API 後端應用程式

在本節中,您會建立 ASP.NET Core Web API 後端來處理裝置註冊,以及將通知傳送至 React Native 行動應用程式。

建立 Web 專案

  1. Visual Studio 中,選取 [ 檔案>新增方案]。

  2. 選取 [.NET Core>應用程式>ASP.NET Core>API>下一步]。

  3. 在 [設定新的 ASP.NET Core Web API] 對話框中,選取 [.NET Core 3.1的目標 Framework]。

  4. 針對 [項目名稱] 輸入 PushDemoApi,然後選取 [建立]。

  5. 開始偵錯 (命令 + 輸入) 測試樣板化應用程式。

    注意

    範本化應用程式已設定為使用 WeatherForecastController 作為 launchUrl。 這會在 [屬性>] launchSettings.json中設定。

    如果系統提示您輸入 無效的開發憑證找到 訊息:

    1. 按兩下 [是 ] 同意執行 'dotnet dev-certs https' 工具來修正此問題。 'dotnet dev-certs https' 工具會提示您輸入憑證的密碼,以及密鑰鏈的密碼。

    2. 當系統提示您安裝並信任新憑證時,按兩下 [],然後輸入密鑰鏈的密碼。

  6. 展開 Controllers 資料夾,然後刪除 WeatherForecastController.cs

  7. 刪除 WeatherForecast.cs

  8. 使用 秘密管理員工具設定本機組態值。 將秘密與解決方案分離,可確保它們最終不會出現在原始檔控制中。 開啟 [終端機 ],然後移至項目檔的目錄,然後執行下列命令:

    dotnet user-secrets init
    dotnet user-secrets set "NotificationHub:Name" <value>
    dotnet user-secrets set "NotificationHub:ConnectionString" <value>
    

    將佔位元值取代為您自己的通知中樞名稱和 連接字串 值。 您已在 建立通知中樞 一節中記下它們。 否則,您可以在 Azure 中查閱它們。

    NotificationHub:Name
    請參閱概頂端 [基本資訊] 摘要中的 [名稱]。

    NotificationHub:ConnectionString
    請參閱存取原則中的 DefaultFullSharedAccessSignature

    注意

    針對生產案例,您可以查看 Azure KeyVault 之類的選項,以安全地儲存 連接字串。 為了簡單起見,秘密會新增至 Azure App 服務 應用程式設定。

使用 API 金鑰驗證用戶端 (選擇性)

API 金鑰不如令牌安全,但本教學課程的目的就已足夠。 您可以透過 ASP.NET 中間件輕鬆地設定 API 金鑰。

  1. API 金鑰 新增至本機組態值。

    dotnet user-secrets set "Authentication:ApiKey" <value>
    

    注意

    您應該將佔位元值取代為您自己的值,並記下它。

  2. 控制 + 單擊PushDemoApi 專案,從 [新增] 選單中選擇 [新增資料夾],然後按兩下 [使用驗證新增] 作為 [資料夾名稱]。

  3. 控制 + 單擊 [驗證] 資料夾,然後從 [新增] 功能表中選擇 [新增檔案...]。

  4. 選取 [一般>空白類別],針對 [名稱] 輸入 ApiKeyAuthOptions.cs,然後按兩下 [新增] 新增下列實作。

    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthOptions : AuthenticationSchemeOptions
        {
            public const string DefaultScheme = "ApiKey";
            public string Scheme => DefaultScheme;
            public string ApiKey { get; set; }
        }
    }
    
  5. 將另一個空白類別新增至名為 ApiKeyAuthHandler.csAuthentication 資料夾,然後新增下列實作。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Text.Encodings.Web;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOptions>
        {
            const string ApiKeyIdentifier = "apikey";
    
            public ApiKeyAuthHandler(
                IOptionsMonitor<ApiKeyAuthOptions> options,
                ILoggerFactory logger,
                UrlEncoder encoder,
                ISystemClock clock)
                : base(options, logger, encoder, clock) {}
    
            protected override Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                string key = string.Empty;
    
                if (Request.Headers[ApiKeyIdentifier].Any())
                {
                    key = Request.Headers[ApiKeyIdentifier].FirstOrDefault();
                }
                else if (Request.Query.ContainsKey(ApiKeyIdentifier))
                {
                    if (Request.Query.TryGetValue(ApiKeyIdentifier, out var queryKey))
                        key = queryKey;
                }
    
                if (string.IsNullOrWhiteSpace(key))
                    return Task.FromResult(AuthenticateResult.Fail("No api key provided"));
    
                if (!string.Equals(key, Options.ApiKey, StringComparison.Ordinal))
                    return Task.FromResult(AuthenticateResult.Fail("Invalid api key."));
    
                var identities = new List<ClaimsIdentity> {
                    new ClaimsIdentity("ApiKeyIdentity")
                };
    
                var ticket = new AuthenticationTicket(
                    new ClaimsPrincipal(identities), Options.Scheme);
    
                return Task.FromResult(AuthenticateResult.Success(ticket));
            }
        }
    }
    

    注意

    驗證處理程式是實作配置行為的類型,在此案例中為自定義 API 金鑰配置。

  6. 將另一個空白類別新增至名為 ApiKeyAuthenticationBuilderExtensions.csAuthentication 資料夾,然後新增下列實作。

    using System;
    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public static class AuthenticationBuilderExtensions
        {
            public static AuthenticationBuilder AddApiKeyAuth(
                this AuthenticationBuilder builder,
                Action<ApiKeyAuthOptions> configureOptions)
            {
                return builder
                    .AddScheme<ApiKeyAuthOptions, ApiKeyAuthHandler>(
                        ApiKeyAuthOptions.DefaultScheme,
                        configureOptions);
            }
        }
    }
    

    注意

    此擴充方法可簡化 Startup.cs 中的中間件組態程序代碼,使其更容易閱讀且通常更容易遵循。

  7. Startup.cs 中,更新 ConfigureServices 方法,以在呼叫服務下方設定 API 密鑰驗證 。AddControllers 方法。

    using PushDemoApi.Authentication;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme;
            options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme;
        }).AddApiKeyAuth(Configuration.GetSection("Authentication").Bind);
    }
    
  8. 仍在Startup.cs中,更新 Configure 方法,以在應用程式的 IApplicationBuilder 上呼叫 UseAuthenticationUseAuthorization 擴充方法。 請確定這些方法會在 UseRouting 和app之前呼叫 。UseEndpoints

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseHttpsRedirection();
    
        app.UseRouting();
    
        app.UseAuthentication();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    

    注意

    呼叫 UseAuthentication 會註冊中間件,此中間件會使用先前從 ConfigureServices) 註冊的驗證配置 (。 這必須先呼叫,才能呼叫任何相依於要驗證之用戶的中間件。

新增相依性並設定服務

ASP.NET Core 支援相依性插入 (DI) 軟體設計模式,這是在類別與其相依性之間達成控制 (IoC) 反向的技術。

使用通知中 樞和後端作業的通知中樞 SDK 會封裝在服務內。 服務會註冊並透過適當的抽象概念提供。

  1. 控制 + 按兩下 [ 相依性 ] 資料夾,然後選擇 [管理 NuGet 套件...]。

  2. 搜尋 Microsoft.Azure.NotificationHubs 並確定已核取。

  3. 按兩下 [新增套件],然後在系統提示您接受授權條款時按兩下 [ 接受 ]。

  4. 控制 + 單擊PushDemoApi 專案,從 [新增] 選單中選擇 [新增資料夾],然後按兩下 [使用模型新增] 作為 [資料夾名稱]。

  5. 控制 + 單擊[模型] 資料夾,然後從 [新增] 功能表中選擇 [新增檔案...]。

  6. 選取 [一般>空白類別],針對 [名稱] 輸入PushTemplates.cs,然後按兩下列實作]。

    namespace PushDemoApi.Models
    {
        public class PushTemplates
        {
            public class Generic
            {
                public const string Android = "{ \"notification\": { \"title\" : \"PushDemo\", \"body\" : \"$(alertMessage)\"}, \"data\" : { \"action\" : \"$(alertAction)\" } }";
                public const string iOS = "{ \"aps\" : {\"alert\" : \"$(alertMessage)\"}, \"action\" : \"$(alertAction)\" }";
            }
    
            public class Silent
            {
                public const string Android = "{ \"data\" : {\"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\"} }";
                public const string iOS = "{ \"aps\" : {\"content-available\" : 1, \"apns-priority\": 5, \"sound\" : \"\", \"badge\" : 0}, \"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\" }";
            }
        }
    }
    

    注意

    這個類別包含此案例所需泛型和無訊息通知的令牌化通知承載。 承載定義於 安裝 外部,以允許實驗,而不需要透過服務更新現有的安裝。 以這種方式處理安裝變更已不在本教學課程的範圍內。 針對生產環境,請考慮 自定義範本

  7. 將另一個空白類別新增至名為 DeviceInstallation.cs 的Models 資料夾,然後新增下列實作。

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class DeviceInstallation
        {
            [Required]
            public string InstallationId { get; set; }
    
            [Required]
            public string Platform { get; set; }
    
            [Required]
            public string PushChannel { get; set; }
    
            public IList<string> Tags { get; set; } = Array.Empty<string>();
        }
    }
    
  8. 將另一個空白類別新增至名為 NotificationRequest.cs 的Models 資料夾,然後新增下列實作。

    using System;
    
    namespace PushDemoApi.Models
    {
        public class NotificationRequest
        {
            public string Text { get; set; }
            public string Action { get; set; }
            public string[] Tags { get; set; } = Array.Empty<string>();
            public bool Silent { get; set; }
        }
    }
    
  9. 將另一個空白類別新增至名為 NotificationHubOptions.cs 的Models 資料夾,然後新增下列實作。

    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class NotificationHubOptions
        {
            [Required]
            public string Name { get; set; }
    
            [Required]
            public string ConnectionString { get; set; }
        }
    }
    
  10. 將新的資料夾新增至名為 ServicesPushDemoApi 專案。

  11. 空白介面新增至名為 INotificationService.cs的服務資料夾,然後新增下列實作。

    using System.Threading;
    using System.Threading.Tasks;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public interface INotificationService
        {
            Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token);
            Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token);
            Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token);
        }
    }
    
  12. 空白類別新增至名為 NotificationHubsService.cs的 Services 資料夾,然後新增下列程式代碼以實作 INotificationService 介面:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public class NotificationHubService : INotificationService
        {
            readonly NotificationHubClient _hub;
            readonly Dictionary<string, NotificationPlatform> _installationPlatform;
            readonly ILogger<NotificationHubService> _logger;
    
            public NotificationHubService(IOptions<NotificationHubOptions> options, ILogger<NotificationHubService> logger)
            {
                _logger = logger;
                _hub = NotificationHubClient.CreateClientFromConnectionString(
                    options.Value.ConnectionString,
                    options.Value.Name);
    
                _installationPlatform = new Dictionary<string, NotificationPlatform>
                {
                    { nameof(NotificationPlatform.Apns).ToLower(), NotificationPlatform.Apns },
                    { nameof(NotificationPlatform.Fcm).ToLower(), NotificationPlatform.Fcm }
                };
            }
    
            public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.Platform) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel))
                    return false;
    
                var installation = new Installation()
                {
                    InstallationId = deviceInstallation.InstallationId,
                    PushChannel = deviceInstallation.PushChannel,
                    Tags = deviceInstallation.Tags
                };
    
                if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform))
                    installation.Platform = platform;
                else
                    return false;
    
                try
                {
                    await _hub.CreateOrUpdateInstallationAsync(installation, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(installationId))
                    return false;
    
                try
                {
                    await _hub.DeleteInstallationAsync(installationId, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token)
            {
                if ((notificationRequest.Silent &&
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
                    (!notificationRequest.Silent &&
                    (string.IsNullOrWhiteSpace(notificationRequest?.Text)) ||
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)))
                    return false;
    
                var androidPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.Android :
                    PushTemplates.Generic.Android;
    
                var iOSPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.iOS :
                    PushTemplates.Generic.iOS;
    
                var androidPayload = PrepareNotificationPayload(
                    androidPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                var iOSPayload = PrepareNotificationPayload(
                    iOSPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                try
                {
                    if (notificationRequest.Tags.Length == 0)
                    {
                        // This will broadcast to all users registered in the notification hub
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, token);
                    }
                    else if (notificationRequest.Tags.Length <= 20)
                    {
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, notificationRequest.Tags, token);
                    }
                    else
                    {
                        var notificationTasks = notificationRequest.Tags
                            .Select((value, index) => (value, index))
                            .GroupBy(g => g.index / 20, i => i.value)
                            .Select(tags => SendPlatformNotificationsAsync(androidPayload, iOSPayload, tags, token));
    
                        await Task.WhenAll(notificationTasks);
                    }
    
                    return true;
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Unexpected error sending notification");
                    return false;
                }
            }
    
            string PrepareNotificationPayload(string template, string text, string action) => template
                .Replace("$(alertMessage)", text, StringComparison.InvariantCulture)
                .Replace("$(alertAction)", action, StringComparison.InvariantCulture);
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, IEnumerable<string> tags, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, tags, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
        }
    }
    

    注意

    提供給 SendTemplateNotificationAsync 的標籤示表達式限製為 20 個標記。 對大部分運算元而言,其限制為 6,但表示式只包含 (|| 的 OU在此情況下) 。 如果要求中有20個以上的標記,則必須將其分割成多個要求。 如需詳細資訊,請參閱 路由和標記表達式 檔。

  13. Startup.cs中,更新 ConfigureServices 方法,將 NotificationHubsService 新增為 INotificationService 的單一實作。

    
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        services.AddSingleton<INotificationService, NotificationHubService>();
    
        services.AddOptions<NotificationHubOptions>()
            .Configure(Configuration.GetSection("NotificationHub").Bind)
            .ValidateDataAnnotations();
    }
    

建立通知 API

  1. 控制 + 按兩下[控制器] 資料夾,然後從 [新增] 選單中選擇 [新增檔案...]。

  2. 選取 [ASP.NET Core>Web API 控制器類別],針對 [名稱] 輸入 NotificationsController,然後按兩下 [新增]。

    注意

    如果您遵循 Visual Studio 2019,請選擇 具有讀取/寫入動作範本的 API 控制器

  3. 將下列命名空間新增至檔案頂端。

    using System.ComponentModel.DataAnnotations;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
  4. 更新樣板化控制器,使其衍生自 ControllerBase ,並使用 ApiController 屬性裝飾。

    [ApiController]
    [Route("api/[controller]")]
    public class NotificationsController : ControllerBase
    {
        // Templated methods here
    }
    

    注意

    Controller 基類提供檢視的支援,但在此案例中不需要這麼做,因此可以改用 ControllerBase。 如果您遵循 Visual Studio 2019,您可以略過此步驟。

  5. 如果您選擇完成 [使用 API 金鑰驗證用戶端] 區段,也應該使用 Authorize 屬性裝飾 NotificationsController

    [Authorize]
    
  6. 更新建構函式以接受 已註冊的 INotificationService 實例做為自變數,並將它指派給只讀成員。

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  7. [屬性] 資料夾) 內的launchSettings.json (中,將 launchUrlweatherforecast 變更為 api/notifications,以符合 RegistrationsControllerRoute 屬性中指定的 URL。

  8. 開始偵錯 (命令 + 輸入) 驗證應用程式正在使用新的 NotificationsController ,並傳回 401 未經授權的 狀態。

    注意

    Visual Studio 可能不會在瀏覽器中自動啟動應用程式。 您將使用 Postman 從這個點開始測試 API。

  9. 在新的 Postman 索引標籤上,將要求設定為 GET。 輸入下列位址,以屬性>launchSettings.json中找到的 HTTPs applicationUrl 取代佔位元 <applicationUrl>

    <applicationUrl>/api/notifications
    

    注意

    applicationUrl 應該是預設配置檔的 『https://localhost:5001』。 如果您在 Windows) 上使用 Visual Studio 2019 中的 IIS (預設值,您應該改用 iisSettings 專案中指定的 applicationUrl。 如果位址不正確,您會收到 404 回應。

  10. 如果您選擇完成 [使用 API 金鑰驗證用戶端 ] 區段,請務必設定要求標頭以包含 apikey 值。

    關鍵 價值
    apikey <your_api_key>
  11. 按兩下 [ 傳送] 按鈕。

    注意

    您應該會收到一些 JSON 內容的 200 OK 狀態。

    如果您收到 SSL 憑證驗證警告,您可以在 [設定] 中切換要求 SSL 憑證驗證 Postman 設定。

  12. 以下列程式代碼取代 NotificationsController.cs 中的樣板化類別方法。

    [HttpPut]
    [Route("installations")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<IActionResult> UpdateInstallation(
        [Required]DeviceInstallation deviceInstallation)
    {
        var success = await _notificationService
            .CreateOrUpdateInstallationAsync(deviceInstallation, HttpContext.RequestAborted);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    
    [HttpDelete()]
    [Route("installations/{installationId}")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<ActionResult> DeleteInstallation(
        [Required][FromRoute]string installationId)
    {
        var success = await _notificationService
            .DeleteInstallationByIdAsync(installationId, CancellationToken.None);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    
    [HttpPost]
    [Route("requests")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<IActionResult> RequestPush(
        [Required]NotificationRequest notificationRequest)
    {
        if ((notificationRequest.Silent &&
            string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
            (!notificationRequest.Silent &&
            string.IsNullOrWhiteSpace(notificationRequest?.Text)))
            return new BadRequestResult();
    
        var success = await _notificationService
            .RequestNotificationAsync(notificationRequest, HttpContext.RequestAborted);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    

建立 API 應用程式

您現在會在 Azure App 服務 中建立 API 應用程式,以裝載後端服務。

  1. 登入 Azure 入口網站

  2. 按兩下 [建立資源],然後搜尋並選擇 [API 應用程式],然後按兩下 [ 建立]。

  3. 更新下列欄位,然後按兩 下列欄位,然後按兩下列欄位。。

    應用程式名稱:
    輸入 API 應用程式的全域唯一名稱

    訂閱:
    選擇您在中建立通知中樞的相同目標 用帳戶。

    資源群組:
    選擇您在中建立通知中樞的相同 資源群組

    App Service 方案/位置:
    建立新的 App Service方案

    注意

    從預設選項變更為包含 SSL 支援的方案。 否則,在使用行動應用程式時,您必須採取適當的步驟,以防止 HTTP 要求遭到封鎖。

    Application Insights:
    請保留建議的選項, (使用該名稱建立新的資源,) 或挑選現有的資源。

  4. 布建 API 應用程式 之後,請瀏覽至該資源。

  5. 記下 [概] 頂端 [基本資訊] 摘要中的 URL 屬性。 此 URL 是您稍後在本教學課程中使用的 後端端點

    注意

    URL 會使用您稍早指定的 API 應用程式名稱,格式 https://<app_name>.azurewebsites.net為 。

  6. 從 [設定) ] 底下的清單中 (選取 [組態]。

  7. 針對下列每個設定,按兩下列每個設定,按兩下列每個設定,按兩下列每個設定,按兩下 [新增應用程式設定] 以輸入 [名稱] 和 [

    名字 價值
    Authentication:ApiKey <api_key_value>
    NotificationHub:Name <hub_name_value>
    NotificationHub:ConnectionString <hub_connection_string_value>

    注意

    這些是您先前在用戶設定中定義的相同設定。 您應該能夠複製這些專案。 只有在您選擇使用 API 金鑰驗證用戶端一節時,才需要 Authentication:ApiKey 設定。 針對生產案例,您可以查看 Azure KeyVault 等選項。 這些已新增為應用程式設定,因此在此案例中為簡單。

  8. 新增所有應用程式設定之後,按兩下 [ 儲存],然後按兩下 [繼續]。

發佈後端服務

接下來,您會將應用程式部署至 API 應用程式,使其可從所有裝置存取。

注意

下列步驟專屬於 Visual Studio for Mac。 如果您在 Windows 上使用 Visual Studio 2019 進行追蹤,發佈流程將會不同。 請參閱發行至 Windows 上的 Azure App 服務

  1. 如果您尚未這麼做,請將組態從 [偵 錯] 變更為 [發行 ]。

  2. 控制 + 單擊PushDemoApi 項目,然後從 [發佈] 功能表中選擇 [發佈至 Azure...]。

  3. 如果系統提示您這麼做,請遵循驗證流程。 使用您在上一個 建立 API 應用程式 一節中使用的帳戶。

  4. 選取您先前從清單中建立的 Azure App 服務 API 應用程式作為發佈目標,然後按兩下 [發佈]。

完成精靈之後,它會將應用程式發佈至 Azure,然後開啟應用程式。 如果您尚未這麼做,請記下 URL 。 此 URL 是您稍後在本教學課程中使用的 後端端點

驗證已發佈的 API

  1. Postman 中開啟新的索引標籤,將要求設定為 PUT ,然後輸入下列位址。 將佔位元取代為您在上一個 發佈後端服務 區段中記下的基位址。

    https://<app_name>.azurewebsites.net/api/notifications/installations
    

    注意

    基位址的格式應為 https://<app_name>.azurewebsites.net/

  2. 如果您選擇完成 [使用 API 金鑰驗證用戶端 ] 區段,請務必設定要求標頭以包含 您的 apikey 值。

    關鍵 價值
    apikey <your_api_key>
  3. 選擇本文的原始選項,然後從格式選項清單中選擇 JSON,然後包含一些佔位元 JSON 內容:

    {}
    
  4. 按兩下 [傳送]。

    注意

    您應該會收到來自服務的 422 UnprocessableEntity 狀態。

  5. 再次執行步驟 1-4,但這次指定要求端點來驗證您收到 400 不正確的要求 回應。

    https://<app_name>.azurewebsites.net/api/notifications/requests
    

注意

目前無法使用有效的要求數據來測試 API,因為這需要來自用戶端應用程式的平臺特定資訊。

建立跨平臺 React Native 應用程式

在本節中,您會建置以跨平臺方式實作推播通知的 React Native 行動應用程式。

它可讓您透過您所建立的後端服務,從通知中樞註冊和取消註冊。

當指定動作且應用程式位於前景時,就會顯示警示。 否則,通知會出現在通知中心。

注意

您通常會在應用程式生命週期的適當時間點執行註冊 (和取消註冊) 動作 (,或作為初次執行) 體驗的一部分,而不需要明確的用戶註冊/取消註冊輸入。 不過,此範例需要明確的使用者輸入,才能更輕鬆地探索及測試這項功能。

建立 React Native解決方案

  1. 在 中Terminal,使用下列命令更新您的環境工具,需要使用 React Native:

    # install node
    brew install node
    # or update
    brew update node
    # install watchman
    brew install watchman
    # or update
    brew upgrade watchman
    # install cocoapods
    sudo gem install cocoapods
    
  2. 在 中 Terminal,如果您已安裝 React Native CLI 以將它卸載,請執行下列命令。 使用 npx 來自動存取可用的最新 React Native CLI 版本:

    npm uninstall -g react-native-cli
    

    注意

    React Native 具有內建命令行介面。 建議您使用 在運行時間使用 npx來安裝及管理特定版本的 CLI,而不是全域安裝及管理特定版本的 CLI,其隨附於 Node.js。 使用 npx react-native <command>時,將會在命令執行時下載並執行目前的穩定 CLI 版本。

  3. 流覽至您要在其中建立新應用程式的項目資料夾。 藉由指定 --template 參數,使用 Typescript 型範本:

    # init new project with npx
    npx react-native init PushDemo --template react-native-template-typescript
    
  4. 執行城市伺服器,它會建置 JavaScript 套件組合並監視任何程式代碼更新,以即時重新整理套件組合:

    cd PushDemo
    npx react-native start
    
  5. 執行 iOS 應用程式以確認設定。 執行下列命令之前,請確定您已啟動 iOS 模擬器或已連線 iOS 裝置:

    npx react-native run-ios
    
  6. 執行 Android 應用程式以確認設定。 需要一些額外的步驟,才能設定 Android 模擬器或裝置,才能存取 React Native 城市伺服器。 下列命令會產生Android的初始JavaScript套件組合,並將其放入assets資料夾中。

    # create assets folder for the bundle
    mkdir android/app/scr/main/assets
    # build the bundle
    npx react-native bundle --platform android --dev true --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res
    # enable ability for sim to access the localhost
    adb reverse tcp:8081 tcp:8081
    

    此文稿將會使用應用程式的初始版本預先部署。 部署之後,請指定伺服器 IP 位址,將您的模擬器或裝置設定為存取城市伺服器。 執行下列命令來建置和執行 Android 應用程式:

    npx react-native run-android
    

    在應用程式中,按 CMD+M (模擬器) 或搖動裝置以填入開發人員設定、流覽至>Change Bundle LocationSettings ,並使用預設埠指定城市伺服器 IP 位址:。 <metro-server-ip-address>:8081

  7. 在檔案中 App.tsx ,將任何變更套用至版面配置、儲存並變更會自動反映在iOS和Android應用程式中。

    注意

    官方檔中提供詳細的開發環境設定指南

安裝必要的套件

您需要下列三個套件,此範例才能運作:

  1. React Native 推播通知 iOS - 專案 GitHub

    當 PushNotificationIOS 從 React Native 的核心分割時,就會建立此套件。 套件會原生實作 iOS 的推播通知,並提供 React Native 介面來存取它。 執行下列命令以安裝套件:

    yarn add @react-native-community/push-notification-ios
    
  2. React Native 推播通知跨平臺

    此套件會以跨平臺方式在iOS和Android上實作本機和遠端通知。 執行下列命令以安裝套件:

    yarn add react-native-push-notification
    
  3. 裝置資訊套件 套件提供運行時間中裝置的相關信息。 使用它來定義裝置標識碼,用來註冊推播通知。 執行下列命令以安裝套件:

    yarn add react-native-device-info
    

實作跨平臺元件

  1. 建立與實作 DemoNotificationHandler

    import PushNotification from 'react-native-push-notification';
    
    class DemoNotificationHandler {
      private _onRegister: any;
      private _onNotification: any;
    
      onNotification(notification: any) {
        console.log('NotificationHandler:', notification);
    
        if (typeof this._onNotification === 'function') {
          this._onNotification(notification);
        }
      }
    
      onRegister(token: any) {
        console.log('NotificationHandler:', token);
    
        if (typeof this._onRegister === 'function') {
          this._onRegister(token);
        }
      }
    
      attachTokenReceived(handler: any) {
        this._onRegister = handler;
      }
    
      attachNotificationReceived(handler: any) {
        this._onNotification = handler;
      }
    }
    
    const handler = new DemoNotificationHandler();
    
    PushNotification.configure({
      onRegister: handler.onRegister.bind(handler),
      onNotification: handler.onNotification.bind(handler),
      permissions: {
        alert: true,
        badge: true,
        sound: true,
      },
      popInitialNotification: true,
      requestPermissions: true,
    });
    
    export default handler;
    
  2. 建立與實作 DemoNotificationService

    import PushNotification from 'react-native-push-notification';
    import DemoNotificationHandler from './DemoNotificationHandler';
    
    export default class DemoNotificationService {
      constructor(onTokenReceived: any, onNotificationReceived: any) {
        DemoNotificationHandler.attachTokenReceived(onTokenReceived);
        DemoNotificationHandler.attachNotificationReceived(onNotificationReceived);
        PushNotification.getApplicationIconBadgeNumber(function(number: number) {
          if(number > 0) {
            PushNotification.setApplicationIconBadgeNumber(0);
          }
        });
      }
    
      checkPermissions(cbk: any) {
        return PushNotification.checkPermissions(cbk);
      }
    
      requestPermissions() {
        return PushNotification.requestPermissions();
      }
    
      cancelNotifications() {
        PushNotification.cancelLocalNotifications();
      }
    
      cancelAll() {
        PushNotification.cancelAllLocalNotifications();
      }
    
      abandonPermissions() {
        PushNotification.abandonPermissions();
      }
    }
    
  3. 建立與實作 DemoNotificationRegistrationService

    export default class DemoNotificationService {
        constructor(
            readonly apiUrl: string,
            readonly apiKey: string) {
        }
    
    async registerAsync(request: any): Promise<Response> {
            const method = 'PUT';
            const registerApiUrl = `${this.apiUrl}/notifications/installations`;
            const result = await fetch(registerApiUrl, {
                method: method,
                headers: {
                    Accept: 'application/json',
                    'Content-Type': 'application/json',
                    'apiKey': this.apiKey
                },
                body: JSON.stringify(request)
            });
    
            this.validateResponse(registerApiUrl, method, request, result);
            return result;
        }
    
        async deregisterAsync(deviceId: string): Promise<Response> {
            const method = 'DELETE';
            const deregisterApiUrl = `${this.apiUrl}/notifications/installations/${deviceId}`;
            const result = await fetch(deregisterApiUrl, {
                method: method,
                headers: {
                    Accept: 'application/json',
                    'Content-Type': 'application/json',
                    'apiKey': this.apiKey
                }
            });
    
            this.validateResponse(deregisterApiUrl, method, null, result);
            return result;
        }
    
        private validateResponse(requestUrl: string, method: string, requestPayload: any, response: Response) {
            console.log(`Request: ${method} ${requestUrl} => ${JSON.stringify(requestPayload)}\nResponse: ${response.status}`);
            if (!response || response.status != 200) {
                throw `HTTP error ${response.status}: ${response.statusText}`;
            }
        }
    }
    
  4. 設定應用程式。 開啟 package.json 並新增下列文稿定義:

    "configure": "cp .app.config.tsx src/config/AppConfig.tsx"
    

    然後執行此文稿,此腳本會將預設組態 config 複製到資料夾。

    yarn configure
    

    最後一個步驟是使用 API 存取資訊更新在上一個步驟複製的組態檔。 指定 apiKeyapiUrl 參數:

    module.exports = {
        appName: "PushDemo",
        env: "production",
        apiUrl: "https://<azure-push-notifications-api-url>/api/",
        apiKey: "<api-auth-key>",
    };
    

實作跨平臺UI

  1. 定義版面配置

    <View style={styles.container}>
      {this.state.isBusy &&
        <ActivityIndicator></ActivityIndicator>
      }
      <View style={styles.button}>
        <Button title="Register" onPress={this.onRegisterButtonPress.bind(this)} disabled={this.state.isBusy} />
      </View>
      <View style={styles.button}>
        <Button title="Deregister" onPress={this.onDeregisterButtonPress.bind(this)} disabled={this.state.isBusy} />
      </View>
    </View>
    
  2. 套用樣式

    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: "center",
        justifyContent: 'flex-end',
        margin: 50,
      },
      button: {
        margin: 5,
        width: "100%",
      }
    });
    
  3. 初始化頁面元件

      state: IState;
      notificationService: DemoNotificationService;
      notificationRegistrationService: DemoNotificationRegistrationService;
      deviceId: string;
    
      constructor(props: any) {
        super(props);
        this.deviceId = DeviceInfo.getUniqueId();
        this.state = {
          status: "Push notifications registration status is unknown",
          registeredOS: "",
          registeredToken: "",
          isRegistered: false,
          isBusy: false,
        };
    
        this.notificationService = new DemoNotificationService(
          this.onTokenReceived.bind(this),
          this.onNotificationReceived.bind(this),
        );
    
        this.notificationRegistrationService = new DemoNotificationRegistrationService(
          Config.apiUrl,
          Config.apiKey,
        );
      }
    
  4. 定義按鈕按下處理程式

      async onRegisterButtonPress() {
        if (!this.state.registeredToken || !this.state.registeredOS) {
          Alert.alert("The push notifications token wasn't received.");
          return;
        }
    
        let status: string = "Registering...";
        let isRegistered = this.state.isRegistered;
        try {
          this.setState({ isBusy: true, status });
          const pnPlatform = this.state.registeredOS == "ios" ? "apns" : "fcm";
          const pnToken = this.state.registeredToken;
          const request = {
            installationId: this.deviceId,
            platform: pnPlatform,
            pushChannel: pnToken,
            tags: []
          };
          const response = await this.notificationRegistrationService.registerAsync(request);
          status = `Registered for ${this.state.registeredOS} push notifications`;
          isRegistered = true;
        } catch (e) {
          status = `Registration failed: ${e}`;
        }
        finally {
          this.setState({ isBusy: false, status, isRegistered });
        }
      }
    
      async onDeregisterButtonPress() {
        if (!this.notificationService)
          return;
    
        let status: string = "Deregistering...";
        let isRegistered = this.state.isRegistered;
        try {
          this.setState({ isBusy: true, status });
          await this.notificationRegistrationService.deregisterAsync(this.deviceId);
          status = "Deregistered from push notifications";
          isRegistered = false;
        } catch (e) {
          status = `Deregistration failed: ${e}`;
        }
        finally {
          this.setState({ isBusy: false, status, isRegistered });
        }
      }
    
  5. 處理收到的令牌註冊和推播通知

      onTokenReceived(token: any) {
        console.log(`Received a notification token on ${token.os}`);
        this.setState({ registeredToken: token.token, registeredOS: token.os, status: `The push notifications token has been received.` });
    
        if (this.state.isRegistered && this.state.registeredToken && this.state.registeredOS) {
          this.onRegisterButtonPress();
        }
      }
    
      onNotificationReceived(notification: any) {
        console.log(`Received a push notification on ${this.state.registeredOS}`);
        this.setState({ status: `Received a push notification...` });
    
        if (notification.data.message) {
          Alert.alert(AppConfig.appName, `${notification.data.action} action received`);
        }
      }
    };
    

設定推播通知的原生 Android 專案

設定必要的Android套件

建置應用程式時會自動 連結 套件。 您有一些額外的步驟可完成設定程式。

設定 Android 指令清單

在 「android/app/src/main/AndroidManifest.xml」中,確認套件名稱、許可權和必要服務。 請確定您已註冊 RNPushNotificationPublisherRNPushNotificationBootEventReceiver 接收者,並註冊 RNPushNotificationListenerService 服務。 通知元數據可用來自定義推播通知外觀。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="YOUR_PACKAGE_NAME">

      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.WAKE_LOCK" />
      <uses-permission android:name="android.permission.VIBRATE" />
      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

      <application
        android:name=".MainApplication"
        android:label="@string/app_name"
        android:usesCleartextTraffic="true"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:allowBackup="false"
        android:theme="@style/AppTheme">

        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_channel_name"
                    android:value="PushDemo Channel"/>
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_channel_description"
                    android:value="PushDemo Channel Description"/>
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_foreground"
                    android:value="true"/>
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_color"
                    android:resource="@android:color/white"/>

        <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
        <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <service
            android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

        <activity
          android:name=".MainActivity"
          android:label="@string/app_name"
          android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
          android:launchMode="singleTask"
          android:windowSoftInputMode="adjustResize">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
        <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
      </application>

</manifest>

設定Google服務

在 “android/app/build.gradle” 中註冊 Google Services:

dependencies {
  ...
  implementation 'com.google.firebase:firebase-analytics:17.3.0'
  ...
}

apply plugin: 'com.google.gms.google-services'

將您在 FCM 安裝期間下載的 「google-services.json」 檔案複製到項目資料夾 「android/app/」。

處理Android的推播通知

您已設定現有的 RNPushNotificationListenerService 服務來處理傳入的 Android 推播通知。 此服務先前已在應用程式指令清單中註冊。 它會處理傳入通知,並將其 Proxy 代理至跨平臺 React Native 部分。 不需要其他步驟。

設定推播通知的原生 iOS 專案

設定必要的 iOS 套件

建置應用程式時 ,會自動連結 套件。 您只需要安裝原生 Pod:

npx pod-install

設定 Info.plist 和 Entitlements.plist

  1. 移至您的 “PushDemo/ios” 資料夾,然後開啟 “PushDemo.xcworkspace” 工作區,選取頂端專案 “PushDemo”,然後選取 [簽署 & 功能] 索引卷標。

  2. 更新套件組合識別碼以符合布建配置檔中使用的值。

  3. 使用 [+] 按鈕新增兩個新的功能:

    • 背景模式功能及刻度遠端通知。
    • 推播通知功能

處理 iOS 的推播通知

  1. 開啟 「AppDelegate.h」,並新增下列匯入:

    #import <UserNotifications/UNUserNotificationCenter.h>
    
  2. 藉由新增 UNUserNotificationCenterDelegate來更新 「AppDelegate」 支援的通訊協定清單:

    @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>
    
  3. 開啟 「AppDelegate.m」,並設定所有必要的 iOS 回呼:

    #import <UserNotifications/UserNotifications.h>
    #import <RNCPushNotificationIOS.h>
    
    ...
    
    // Required to register for notifications
    - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
    {
     [RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
    }
    
    // Required for the register event.
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
     [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
    }
    
    // Required for the notification event. You must call the completion handler after handling the remote notification.
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
    {
      [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
    }
    
    // Required for the registrationError event.
    - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
    {
     [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
    }
    
    // IOS 10+ Required for localNotification event
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center
    didReceiveNotificationResponse:(UNNotificationResponse *)response
             withCompletionHandler:(void (^)(void))completionHandler
    {
      [RNCPushNotificationIOS didReceiveNotificationResponse:response];
      completionHandler();
    }
    
    // IOS 4-10 Required for the localNotification event.
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {
     [RNCPushNotificationIOS didReceiveLocalNotification:notification];
    }
    
    //Called when a notification is delivered to a foreground app.
    -(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
    {
      completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
    }
    

測試解決方案

您現在可以透過後端服務測試傳送通知。

傳送測試通知

  1. Postman 中開啟新的索引標籤。

  2. 將要求設定為 POST,然後輸入下列位址:

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. 如果您選擇完成 [使用 API 金鑰驗證用戶端 ] 區段,請務必設定要求標頭以包含 apikey 值。

    關鍵 價值
    apikey <your_api_key>
  4. 選擇 Body的原始選項,然後從格式選項清單中選擇 JSON,然後包含一些佔位元 JSON 內容:

    {
        "text": "Message from Postman!",
        "action": "action_a"
    }
    
  5. 選取 [ 程序代碼] 按鈕,其位於視窗右上方的 [ 儲存 ] 按鈕底下。 根據您是否包含 apikey標頭 () 而定,要求看起來應該類似下列範例:

    POST /api/notifications/requests HTTP/1.1
    Host: https://<app_name>.azurewebsites.net
    apikey: <your_api_key>
    Content-Type: application/json
    
    {
        "text": "Message from backend service",
        "action": "action_a"
    }
    
  6. (AndroidiOS) ,在一或兩個目標平台上執行 PushDemo 應用程式。

    注意

    如果您要在 Android 上測試,請確定您未在 [ 錯] 中執行,或執行應用程式來部署應用程式,然後強制關閉應用程式,然後從啟動器再次啟動它。

  7. PushDemo 應用程式中,點選 [ 註冊] 按鈕。

  8. 回到 Postman,如果您尚未) 按兩下 [傳送] 按鈕,請關閉 [產生代碼段] 視窗 (。

  9. 驗證您在 Postman 中收到 200 OK 回應,且警示會出現在應用程式中,其中顯示已收到的 ActionA 動作

  10. 關閉 PushDemo 應用程式,然後在 Postman 中再次按兩下 [傳送] 按鈕。

  11. 再次驗證您在Postman中收到200 OK回應。 使用正確的訊息,驗證 PushDemo 應用程式的通知出現在通知區域中。

  12. 點選通知以確認它已開啟應用程式並顯示 ActionA 動作已收到 警示。

  13. 回到Postman,修改先前的要求本文以傳送無訊息通知,以指定動作action_b而不是action_a

    {
        "action": "action_b",
        "silent": true
    }
    
  14. 在應用程式仍然開啟時,按兩下Postman中的 [傳送] 按鈕。

  15. 驗證您在 Postman 中收到 200 OK 回應,且警示出現在應用程式中,其中顯示已收到的 ActionB 動作,而不是收到 ActionA 動作

  16. 關閉 PushDemo 應用程式,然後在 Postman 中再次按兩下 [傳送] 按鈕。

  17. 驗證您在 Postman 中收到 200 OK 回應,且無訊息通知不會出現在通知區域中。

故障排除

後端服務沒有回應

在本機測試時,請確定後端服務正在執行,並使用正確的埠。

如果針對 Azure API 應用程式進行測試,請檢查服務是否正在執行,且已部署且已啟動,且未發生錯誤。

請務必在 Postman 或透過客戶端測試時,在行動裝置應用程式組態中正確指定基位址。 基地址應該表示在本機測試時為 https://<api_name>.azurewebsites.net/https://localhost:5001/

啟動或停止偵錯會話之後,未在Android上收到通知

請確定您在啟動或停止偵錯會話之後再次註冊。 調試程式會導致產生新的 Firebase 令牌。 通知中樞安裝也必須更新。

從後端服務接收 401 狀態代碼

驗證您正在設定 apikey 要求標頭,且此值符合您為後端服務設定的值。

如果您在本機測試時收到此錯誤,請確定您在用戶端設定中定義的密鑰值符合 API 所使用的 Authentication:ApiKey 用戶設定值。

如果您要使用 API 應用程式進行測試,請確定用戶端組態檔中的密鑰值符合您在 API 應用程式中使用的 Authentication:ApiKey 應用程式設定。

注意

如果您在部署後端服務之後已建立或變更此設定,則必須重新啟動服務,才能生效。

如果您選擇不完成 [使用 API 金鑰驗證用戶端 ] 區段,請確定您未將 Authorize 屬性套用至 NotificationsController 類別。

從後端服務接收 404 狀態代碼

驗證端點和 HTTP 要求方法是否正確。 例如,端點應指出為:

  • [PUT]https://<api_name>.azurewebsites.net/api/notifications/installations
  • [DELETE]https://<api_name>.azurewebsites.net/api/notifications/installations/<installation_id>
  • [POST]https://<api_name>.azurewebsites.net/api/notifications/requests

或在本機測試時:

  • [PUT]https://localhost:5001/api/notifications/installations
  • [DELETE]https://localhost:5001/api/notifications/installations/<installation_id>
  • [POST]https://localhost:5001/api/notifications/requests

在用戶端應用程式中指定基位址時,請確定其結尾為 /。 基地址應該表示在本機測試時為 https://<api_name>.azurewebsites.net/https://localhost:5001/

無法註冊並顯示通知中樞錯誤訊息

確認測試裝置具有網路連線能力。 然後,藉由設定斷點來檢查 HttpResponse 中的 StatusCode 屬性值,以判斷 Http 回應狀態代碼。

根據狀態代碼檢閱先前的疑難解答建議。

在傳回個別 API 之這些特定狀態代碼的行上設定斷點。 然後在本機偵錯時嘗試呼叫後端服務。

使用適當的承載,驗證後端服務是否如預期般運作。 針對有問題的平臺,使用用戶端程式代碼所建立的實際承載。

檢閱平臺特定的組態區段,以確保未遺漏任何步驟。 檢查是否要針對 installation id 適當的平臺解析適當的值和 token 變數。

無法解析裝置錯誤訊息的識別碼

檢閱平臺特定的組態區段,以確保未遺漏任何步驟。

後續步驟

您現在應該會有透過後端服務連線到通知中樞的基本 React Native 應用程式,而且可以傳送和接收通知。

您可能需要調整本教學課程中使用的範例,以符合您自己的案例。 也建議實作更健全的錯誤處理、重試邏輯和記錄。

Visual Studio App Center 可快速併入行動裝置應用程式中,提供分析和診斷來協助進行疑難解答。