教學課程:使用 Azure 通知中樞將通知傳送給特定使用者Tutorial: Send notifications to specific users by using Azure Notification Hubs

概觀Overview

本教學課程將示範如何使用 Azure 通知中心,來將推播通知傳送到特定裝置上的特定應用程式使用者。This tutorial shows you how to use Azure Notification Hubs to send push notifications to a specific app user on a specific device. ASP.NET WebAPI 後端是用來驗證用戶端。An ASP.NET WebAPI backend is used to authenticate clients. 後端在驗證用戶端應用程式使用者時,會自動將標記新增至通知登錄。When the backend authenticates a client application user, it automatically adds a tag to the notification registration. 後端會使用此標記將通知傳送給特定使用者。The backend uses this tag to send notifications to the specific user.

注意

您可以在 GitHub 上找到本教學課程的完整程式碼。The completed code for this tutorial can be found on GitHub.

在本教學課程中,您會執行下列步驟:In this tutorial, you take the following steps:

  • 建立 WebAPI 專案Create the WebAPI project
  • 對 WebAPI 後端驗證用戶端Authenticate clients to the WebAPI backend
  • 使用 WebAPI 後端來註冊通知Register for notifications by using the WebAPI backend
  • 從 WebAPI 後端傳送通知Send notifications from the WebAPI backend
  • 發佈新的 WebAPI 後端Publish the new WebAPI backend
  • 更新用戶端專案的程式碼Update the code for the client project
  • 測試應用程式Test the application

必要條件Prerequisites

本教學課程以您在以下教學課程中建立的通知中樞和 Visual Studio 專案為基礎:教學課程:使用 Azure 通知中樞將通知傳送至通用 Windows 平台應用程式This tutorial builds on the notification hub and Visual Studio project that you created in the Tutorial: Send notifications to Universal Windows Platform apps by using Azure Notification Hubs tutorial. 因此,請先加以完成,再開始進行本教學課程。Therefore, complete it before starting on this tutorial.

注意

如果您使用 Azure App Service 中的 Mobile Apps 作為後端服務,請參閱本教學課程的 Mobile Apps 版本If you are using Mobile Apps in Azure App Service as your backend service, see the Mobile Apps version of this tutorial.

建立 WebAPI 專案Create the WebAPI project

下列各節討論如何建立新的 ASP.NET WebAPI 後端。The following sections discuss the creation of a new ASP.NET WebAPI backend. 此程序有三個主要用途:This process has three main purposes:

  • 驗證用戶端:您可以新增訊息處理常式來驗證用戶端要求,並將使用者與要求建立關聯。Authenticate clients: You add a message handler to authenticate client requests and associate the user with the request.
  • 使用 WebAPI 後端來註冊通知:您會新增控制器來處理新的註冊,以便用戶端裝置接收通知。Register for notifications by using the WebAPI backend: You add a controller to handle new registrations for a client device to receive notifications. 經過驗證的使用者名稱會自動新增至註冊作為 標記The authenticated username is automatically added to the registration as a tag.
  • 傳送通知給用戶端:您可新增一個控制器,以便使用者對與標籤建立關聯的裝置和用戶端觸發安全的推送。Send notifications to clients: You add a controller to provide a way for users to trigger a secure push to devices and clients associated with the tag.

執行下列動作,建立新的 ASP.NET WebAPI 後端:Create the new ASP.NET WebAPI backend by doing the following actions:

重要

如果您使用 Visual Studio 2015 或更新版本,在開始本教學課程之前,請確定您已安裝適用於 Visual Studio 的最新版 NuGet 套件管理員。If you are using Visual Studio 2015 or earlier, before starting this tutorial, ensure that you have installed the latest version of NuGet Package Manager for Visual Studio.

若要檢查版本,請啟動 Visual Studio。To check, start Visual Studio. 在 [工具] 功能表上,選取 [擴充功能和更新] 。On the Tools menu, select Extensions and Updates. 搜尋您的 Visual Studio 版本中的 NuGet Package Manager,然後確定您已安裝最新版本。Search for NuGet Package Manager in your version of Visual Studio, and make sure you have the latest version. 如果您的版本不是最新版本,請將它解除安裝,然後重新安裝 NuGet 套件管理員。If your version is not the latest version, uninstall it, and then reinstall the NuGet Package Manager.

注意

確定您已安裝 Visual Studio Azure SDK 以供網站部署。Make sure you have installed the Visual Studio Azure SDK for website deployment.

  1. 啟動 Visual Studio 或 Visual Studio Express。Start Visual Studio or Visual Studio Express.

  2. 選取 [伺服器總管] ,然後登入您的 Azure 帳戶。Select Server Explorer, and sign in to your Azure account. 若要在您的帳戶上建立網站資源,您必須登入。To create the web site resources on your account, you must be signed in.

  3. 在 Visual Studio 中,以滑鼠右鍵按一下 Visual Studio 解決方案,並指向 [新增] ,然後按一下 [新增專案] 。In Visual Studio, right-click Visual Studio solution, point to Add, and click New Project.

  4. 展開 [Visual C#] ,並選取 [Web] ,然後按一下 [ASP.NET Web 應用程式] 。Expand Visual C#, select Web, and click ASP.NET Web Application.

  5. 在 [名稱] 方塊中,輸入 AppBackend,然後選取 [確定] 。In the Name box, type AppBackend, and then select OK.

    [新增專案] 視窗

  6. 在 [新增 ASP.NET 專案] 視窗中,選取 [Web API] 核取方塊,然後選取 [確定] 。In the New ASP.NET Project window, select the Web API check box, and then select OK.

    [新增 ASP.NET 專案] 視窗

  7. 在 [設定 Microsoft Azure Web 應用程式] 視窗中,選取訂用帳戶,然後在 [App Service 方案] 清單中,執行下列其中一個動作:In the Configure Microsoft Azure Web App window, select a subscription and then, in the App Service plan list, do either of the following actions:

    • 選取您已建立的 App Service 方案。Select an app service plan that you've already created.
    • 選取 [建立新的 App Service 方案] ,然後建立一個新方案。Select Create a new app service plan, and then create one.

    在此教學課程中您不需要資料庫。You do not need a database for this tutorial. 在您選取 App Service 方案之後,選取 [確定] 來建立專案。After you have selected your app service plan, select OK to create the project.

    [定 Microsoft Azure Web 應用程式] 視窗

    如果您沒有看到這個用於設定 App Service 方案的頁面,請繼續進行本教學課程。If you don't see this page for configure app service plan, continue with the tutorial. 您可以在稍後發佈應用程式時再進行設定。You can configure it while publishing the app later.

對 WebAPI 後端驗證用戶端Authenticate clients to the WebAPI backend

在本節中,您會為新的後端建立名為 AuthenticationTestHandler 的新訊息處理常式類別。In this section, you create a new message-handler class named AuthenticationTestHandler for the new backend. 這個類別衍生自 DelegatingHandler 並新增為訊息處理常式,以便處理進入後端的所有要求。This class is derived from DelegatingHandler and added as a message handler so that it can process all requests that come into the backend.

  1. 在 [方案總管] 中,以滑鼠右鍵按一下 [AppBackend] 專案,然後依序選取 [新增] 和 [類別] 。In Solution Explorer, right-click the AppBackend project, select Add, and then select Class.

  2. 將新類別命名為 AuthenticationTestHandler.cs,然後選取 [新增] 以產生類別。Name the new class AuthenticationTestHandler.cs, and then select Add to generate the class. 為了簡單起見,此類別使用「基本驗證」 來驗證使用者。This class authenticates users by using Basic Authentication for simplicity. 您的應用程式可以使用任何驗證結構描述。Your app can use any authentication scheme.

  3. 在 AuthenticationTestHandler.cs 中,加入下列 using 陳述式:In AuthenticationTestHandler.cs, add the following using statements:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. 在 AuthenticationTestHandler.cs 中,以下列程式碼取代 AuthenticationTestHandler 類別定義:In AuthenticationTestHandler.cs, replace the AuthenticationTestHandler class definition with the following code:

    下列三個條件都成立時,此處理常式會授權要求:The handler authorizes the request when the following three conditions are true:

    • 要求包含「授權」 標頭。The request includes an Authorization header.
    • 要求使用 基本 驗證。The request uses basic authentication.
    • 使用者名稱字串和密碼字串是相同的字串。The user name string and the password string are the same string.

    否則,會拒絕此要求。Otherwise, the request is rejected. 此驗證不是真正的驗證和授權方法。This authentication is not a true authentication and authorization approach. 這只是本教學課程的簡單範例。It is only a simple example for this tutorial.

    如果要求訊息已經由 AuthenticationTestHandler 驗證及授權,則基本驗證使用者會附加至 HttpContext 上的目前要求。If the request message is authenticated and authorized by AuthenticationTestHandler, the basic authentication user is attached to the current request on HttpContext. 稍後另一個控制器 (RegisterController) 會使用 HttpContext 中的使用者資訊,將 標記 新增至通知註冊要求。User information in HttpContext will be used by another controller (RegisterController) later to add a tag to the notification registration request.

    public class AuthenticationTestHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var authorizationHeader = request.Headers.GetValues("Authorization").First();
    
            if (authorizationHeader != null && authorizationHeader
                .StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase))
            {
                string authorizationUserAndPwdBase64 =
                    authorizationHeader.Substring("Basic ".Length);
                string authorizationUserAndPwd = Encoding.Default
                    .GetString(Convert.FromBase64String(authorizationUserAndPwdBase64));
                string user = authorizationUserAndPwd.Split(':')[0];
                string password = authorizationUserAndPwd.Split(':')[1];
    
                if (verifyUserAndPwd(user, password))
                {
                    // Attach the new principal object to the current HttpContext object
                    HttpContext.Current.User =
                        new GenericPrincipal(new GenericIdentity(user), new string[0]);
                    System.Threading.Thread.CurrentPrincipal =
                        System.Web.HttpContext.Current.User;
                }
                else return Unauthorized();
            }
            else return Unauthorized();
    
            return base.SendAsync(request, cancellationToken);
        }
    
        private bool verifyUserAndPwd(string user, string password)
        {
            // This is not a real authentication scheme.
            return user == password;
        }
    
        private Task<HttpResponseMessage> Unauthorized()
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }
    }
    

    注意

    安全性注意事項:AuthenticationTestHandler 類別未提供真正的驗證。Security note: The AuthenticationTestHandler class does not provide true authentication. 它僅可用於模仿基本驗證而且並不安全。It is used only to mimic basic authentication and is not secure. 您必須在生產應用程式和服務中實作安全的驗證機制。You must implement a secure authentication mechanism in your production applications and services.

  5. 若要註冊訊息處理常式,請在 App_Start/WebApiConfig.cs 類別中 Register 方法的結尾新增下列程式碼:To register the message handler, add the following code at the end of the Register method in the App_Start/WebApiConfig.cs class:

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. 儲存您的變更。Save your changes.

使用 WebAPI 後端來註冊通知Register for notifications by using the WebAPI backend

在本節中,您會將新的控制器新增至 WebAPI 後端來處理要求,以使用通知中樞的用戶端程式庫為使用者和裝置註冊通知。In this section, you add a new controller to the WebAPI backend to handle requests to register a user and a device for notifications by using the client library for notification hubs. 控制器會對已由 AuthenticationTestHandler 驗證並附加至 HttpContext 的使用者,新增使用者標記。The controller adds a user tag for the user that was authenticated and attached to HttpContext by AuthenticationTestHandler. 此標籤具有以下字串格式:"username:<actual username>"The tag has the string format, "username:<actual username>".

  1. 在 [方案總管] 中,以滑鼠右鍵按一下 [AppBackend] 專案,然後選取 [管理 NuGet 套件] 。In Solution Explorer, right-click the AppBackend project and then select Manage NuGet Packages.

  2. 在左窗格中,選取 [線上] ,然後在 [搜尋] 方塊中輸入 Microsoft.Azure.NotificationHubsIn the left pane, select Online and then, in the Search box, type Microsoft.Azure.NotificationHubs.

  3. 選取結果清單中的 [Microsoft Azure 通知中樞] ,然後選取 [安裝] 。In the results list, select Microsoft Azure Notification Hubs, and then select Install. 請完成安裝,然後關閉 [NuGet Package Manager] 視窗。Complete the installation, and then close the NuGet Package Manager window.

    此動作會使用 Microsoft.Azure.Notification Hubs NuGet 套件來新增對 Azure 通知中樞 SDK 的參考。This action adds a reference to the Azure Notification Hubs SDK by using the Microsoft.Azure.Notification Hubs NuGet package.

  4. 建立新的類別檔案,代表與用來傳送通知的通知中樞間的連線。Create a new class file that represents the connection with the notification hub that's used to send notifications. 在 [方案總管] 中,以滑鼠右鍵按一下 Models 資料夾,選取 [新增] ,然後選取 [類別] 。In Solution Explorer, right-click the Models folder, select Add, and then select Class. 將新類別命名為 Notifications.cs,然後選取 [新增] 以產生類別。Name the new class Notifications.cs, and then select Add to generate the class.

    [新增項目] 視窗

  5. 在 Notifications.cs 中,將下列 using 陳述式新增在檔案頂端:In Notifications.cs, add the following using statement at the top of the file:

    using Microsoft.Azure.NotificationHubs;
    
  6. 以下列程式碼取代 Notifications 類別定義,並以通知中樞的連接字串 (含完整存取權) 和中心名稱 (可在 Azure 入口網站取代) 取代兩個預留位置:Replace the Notifications class definition with the following code, and replace the two placeholders with the connection string (with full access) for your notification hub and the hub name (available at Azure portal):

    public class Notifications
    {
        public static Notifications Instance = new Notifications();
    
        public NotificationHubClient Hub { get; set; }
    
        private Notifications() {
            Hub = NotificationHubClient.CreateClientFromConnectionString("<your hub's DefaultFullSharedAccessSignature>",
                                                                            "<hub name>");
        }
    }
    

    重要

    請輸入中樞的名稱DefaultFullSharedAccessSignature,再繼續進行。Enter the name and the DefaultFullSharedAccessSignature of your hub before proceeding further.

  7. 接下來,建立名為 RegisterController 的新控制器。Next, create a new controller named RegisterController. 在 [方案總管] 中,以滑鼠右鍵按一下 Controllers 資料夾,選取 [新增] ,然後選取 [控制器] 。In Solution Explorer, right-click the Controllers folder, select Add, and then select Controller.

  8. 選取 [Web API 2 控制器 - 空的] ,然後選取 [新增] 。Select Web API 2 Controller - Empty, and then select Add.

    [新增 Scaffold] 視窗

  9. 在 [控制器名稱] 方塊中,輸入 RegisterController 為新的類別命名,然後選取 [新增] 。In the Controller name box, type RegisterController to name the new class, and then select Add.

    [新增控制器] 視窗

  10. 在 RegisterController.cs 中,加入下列 using 陳述式:In RegisterController.cs, add the following using statements:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. RegisterController 類別定義中加入下列程式碼。Add the following code inside the RegisterController class definition. 在此程式碼中,您會為已附加至 HttpContext 的使用者新增使用者標籤。In this code, you add a user tag for the user that's attached to HttpContext. 您新增的訊息篩選 AuthenticationTestHandler 會驗證此使用者並附加至 HttpContext。The user was authenticated and attached to HttpContext by the message filter that you added, AuthenticationTestHandler. 您也可以新增選擇性檢查,以驗證使用者是否有權註冊所要求的標籤。You can also add optional checks to verify that the user has rights to register for the requested tags.

    private NotificationHubClient hub;
    
    public RegisterController()
    {
        hub = Notifications.Instance.Hub;
    }
    
    public class DeviceRegistration
    {
        public string Platform { get; set; }
        public string Handle { get; set; }
        public string[] Tags { get; set; }
    }
    
    // POST api/register
    // This creates a registration id
    public async Task<string> Post(string handle = null)
    {
        string newRegistrationId = null;
    
        // make sure there are no existing registrations for this push handle (used for iOS and Android)
        if (handle != null)
        {
            var registrations = await hub.GetRegistrationsByChannelAsync(handle, 100);
    
            foreach (RegistrationDescription registration in registrations)
            {
                if (newRegistrationId == null)
                {
                    newRegistrationId = registration.RegistrationId;
                }
                else
                {
                    await hub.DeleteRegistrationAsync(registration);
                }
            }
        }
    
        if (newRegistrationId == null) 
            newRegistrationId = await hub.CreateRegistrationIdAsync();
    
        return newRegistrationId;
    }
    
    // PUT api/register/5
    // This creates or updates a registration (with provided channelURI) at the specified id
    public async Task<HttpResponseMessage> Put(string id, DeviceRegistration deviceUpdate)
    {
        RegistrationDescription registration = null;
        switch (deviceUpdate.Platform)
        {
            case "mpns":
                registration = new MpnsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "wns":
                registration = new WindowsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "apns":
                registration = new AppleRegistrationDescription(deviceUpdate.Handle);
                break;
            case "fcm":
                registration = new FcmRegistrationDescription(deviceUpdate.Handle);
                break;
            default:
                throw new HttpResponseException(HttpStatusCode.BadRequest);
        }
    
        registration.RegistrationId = id;
        var username = HttpContext.Current.User.Identity.Name;
    
        // add check if user is allowed to add these tags
        registration.Tags = new HashSet<string>(deviceUpdate.Tags);
        registration.Tags.Add("username:" + username);
    
        try
        {
            await hub.CreateOrUpdateRegistrationAsync(registration);
        }
        catch (MessagingException e)
        {
            ReturnGoneIfHubResponseIsGone(e);
        }
    
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    // DELETE api/register/5
    public async Task<HttpResponseMessage> Delete(string id)
    {
        await hub.DeleteRegistrationAsync(id);
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    private static void ReturnGoneIfHubResponseIsGone(MessagingException e)
    {
        var webex = e.InnerException as WebException;
        if (webex.Status == WebExceptionStatus.ProtocolError)
        {
            var response = (HttpWebResponse)webex.Response;
            if (response.StatusCode == HttpStatusCode.Gone)
                throw new HttpRequestException(HttpStatusCode.Gone.ToString());
        }
    }
    
  12. 儲存您的變更。Save your changes.

從 WebAPI 後端傳送通知Send notifications from the WebAPI backend

在本節中,您會新增控制器,以便用戶端裝置傳送通知。In this section, you add a new controller that exposes a way for client devices to send a notification. 此通知是以使用者名稱標籤為基礎,其使用 ASP.NET WebAPI 後端中的 Azure 通知中樞 .NET 程式庫。The notification is based on the username tag that uses Azure Notification Hubs .NET Library in the ASP.NET WebAPI backend.

  1. 以您在上一節中建立 RegisterController 的相同方式,建立另一個名為 NotificationsController的新控制器。Create another new controller named NotificationsController the same way you created RegisterController in the previous section.

  2. 在 NotificationsController.cs 中,加入下列 using 陳述式:In NotificationsController.cs, add the following using statements:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. NotificationsController 類別中新增下列方法:Add the following method to the NotificationsController class:

    此程式碼會傳送以平台通知服務 (PNS) pns 參數為基礎的通知類型。This code sends a notification type that's based on the Platform Notification Service (PNS) pns parameter. to_tag 的值用來設定訊息上的 username 標記。The value of to_tag is used to set the username tag on the message. 此標記必須符合作用中通知中樞註冊的使用者名稱標記。This tag must match a username tag of an active notification hub registration. 通知訊息是取自 POST 要求主體,並針對目標 PNS 格式化。The notification message is pulled from the body of the POST request and formatted for the target PNS.

    視您的支援裝置用來接收通知的 PNS 而言,可支援各種格式的通知。Depending on the PNS that your supported devices use to receive notifications, the notifications are supported by a variety of formats. 例如在 Windows 裝置上,您可以搭配 WNS 使用不受其他 PNS 直接支援的快顯通知For example, on Windows devices, you might use a toast notification with WNS that isn't directly supported by another PNS. 在這類情況下,您的後端必須針對您打算支援的裝置 PNS,將通知格式化為支援的通知。In such an instance, your backend needs to format the notification into a supported notification for the PNS of devices you plan to support. 然後在 NotificationHubClient 類別 上使用適當的傳送 API。Then use the appropriate send API on the NotificationHubClient class.

    public async Task<HttpResponseMessage> Post(string pns, [FromBody]string message, string to_tag)
    {
        var user = HttpContext.Current.User.Identity.Name;
        string[] userTag = new string[2];
        userTag[0] = "username:" + to_tag;
        userTag[1] = "from:" + user;
    
        Microsoft.Azure.NotificationHubs.NotificationOutcome outcome = null;
        HttpStatusCode ret = HttpStatusCode.InternalServerError;
    
        switch (pns.ToLower())
        {
            case "wns":
                // Windows 8.1 / Windows Phone 8.1
                var toast = @"<toast><visual><binding template=""ToastText01""><text id=""1"">" + 
                            "From " + user + ": " + message + "</text></binding></visual></toast>";
                outcome = await Notifications.Instance.Hub.SendWindowsNativeNotificationAsync(toast, userTag);
                break;
            case "apns":
                // iOS
                var alert = "{\"aps\":{\"alert\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendAppleNativeNotificationAsync(alert, userTag);
                break;
            case "fcm":
                // Android
                var notif = "{ \"data\" : {\"message\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendFcmNativeNotificationAsync(notif, userTag);
                break;
        }
    
        if (outcome != null)
        {
            if (!((outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Abandoned) ||
                (outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Unknown)))
            {
                ret = HttpStatusCode.OK;
            }
        }
    
        return Request.CreateResponse(ret);
    }
    
  4. 若要執行應用程式並確保工作到目前為止的準確性,請選取 F5 鍵。To run the application and ensure the accuracy of your work so far, select the F5 key. 應用程式會開啟網頁瀏覽器並顯示於 ASP.NET 首頁上。The app opens a web browser, and it is displayed on the ASP.NET home page.

發佈新的 WebAPI 後端Publish the new WebAPI backend

接下來,您可將應用程式部署到 Azure 網站,讓它得以從所有裝置存取。Next, you deploy the app to an Azure website to make it accessible from all devices.

  1. 以滑鼠右鍵按一下 AppBackend 專案,然後選取 [發佈] 。Right-click the AppBackend project, and then select Publish.

  2. 選取 [Microsoft Azure App Service] 作為發佈目標,然後選取 [發佈]。Select Microsoft Azure App Service as your publish target, and then select **Publish. [建立 App Service] 視窗隨即開啟。The Create App Service window opens. 您可以在此建立在 Azure 中執行 ASP.NET Web 應用程式所需的所有 Azure 資源。Here you can create all the necessary Azure resources to run the ASP.NET web app in Azure.

    [Microsoft Azure App Service] 圖格

  3. 在 [建立 App Service] 視窗中,選取您的 Azure 帳戶。In the Create App Service window, select your Azure account. 選取 [變更類型] > [Web 應用程式] 。Select Change Type > Web App. 保留預設 [Web 應用程式名稱] ,然後選取 [訂用帳戶] 、[資源群組] 和 [App Service 方案] 。Keep the default Web App Name, and then select the Subscription, Resource Group, and App Service Plan.

  4. 選取 [建立] 。Select Create.

  5. 記下 [摘要] 區段中的 [網站 URL] 屬性。Make a note of the Site URL property in the Summary section. 此 URL 是您在本教學課程中稍後使用的「後端端點」 。This URL is your back-end endpoint later in the tutorial.

  6. 選取 [發佈] 。Select Publish.

精靈完成後,它會將 ASP.NET Web 應用程式發佈至 Azure,然後在預設瀏覽器中開啟應用程式。After you've completed the wizard, it publishes the ASP.NET web app to Azure and then opens the app in the default browser. 您的應用程式可在 Azure App Service 中檢視。Your application is viewable in Azure App Services.

URL 會使用您稍早指定的 Web 應用程式名稱,其格式為 http://<app_name>.azurewebsites.net。The URL uses the web app name that you specified earlier, with the format http://<app_name>.azurewebsites.net.

更新 UWP 用戶端的程式碼Update the code for the UWP client

在本節中,您會更新已針對以下教學課程完成之專案中的程式碼:教學課程:使用 Azure 通知中樞將通知傳送至通用 Windows 平台應用程式In this section, you update the code in the project you completed for the Tutorial: Send notifications to Universal Windows Platform apps by using Azure Notification Hubs tutorial. 此專案應已與 Windows 市集產生關聯。The project should already be associated with the Windows store. 此外它也應設定成使用您的通知中樞。It also should be configured to use your notification hub. 在本節中,您會新增程式碼以呼叫新的 WebAPI 後端,並使用它來註冊和傳送通知。In this section, you add code to call the new WebAPI backend and use it for registering and sending notifications.

  1. 在 Visual Studio 中,開啟您為以下教學課程所建立的方案:教學課程:使用 Azure 通知中樞將通知傳送至通用 Windows 平台應用程式In Visual Studio, open the solution you created for the Tutorial: Send notifications to Universal Windows Platform apps by using Azure Notification Hubs.

  2. 在 [方案總管] 中,以滑鼠右鍵按一下 [通用 Windows 平台 (UWP)] 專案,然後按一下 [管理 NuGet 套件] 。In Solution Explorer, right-click the Universal Windows Platform (UWP) project and then click Manage NuGet Packages.

  3. 選取左邊的 [瀏覽] 。On the left-hand side, select Browse.

  4. 在 [搜尋] 方塊中,輸入 Http ClientIn the Search box, type Http Client.

  5. 在 [結果] 清單中按一下 System.Net.Http,然後按一下 [安裝] 。In the results list, click System.Net.Http, and click Install. 完成安裝。Complete the installation.

  6. 回到 NuGet [搜尋] 方塊,輸入 Json.netBack in the NuGet Search box, type Json.net. 安裝 Newtonsoft.json 套件,然後關閉 [NuGet 套件管理員] 視窗。Install the Newtonsoft.json package, and then close the NuGet Package Manager window.

  7. 在方案總管的 WindowsApp 專案中按兩下 MainPage.xaml,在 Visual Studio 編輯器中開啟該檔案。In Solution Explorer, in the WindowsApp project, double-click MainPage.xaml to open it in the Visual Studio editor.

  8. MainPage.xaml XML 程式碼中,使用下列程式碼取代 <Grid> 區段:此程式碼會新增使用者用來進行驗證的使用者名稱和密碼文字方塊。In the MainPage.xaml XML code, replace the <Grid> section with the following code: This code adds a username and password textbox that the user authenticates with. 它也會新增通知訊息的文字方塊,以及應接收通知的使用者名稱標記:It also adds text boxes for the notification message and the username tag that should receive the notification:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
    
        <TextBlock Grid.Row="0" Text="Notify Users" HorizontalAlignment="Center" FontSize="48"/>
    
        <StackPanel Grid.Row="1" VerticalAlignment="Center">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Row="0" Grid.ColumnSpan="3" Text="Username" FontSize="24" Margin="20,0,20,0"/>
                <TextBox Name="UsernameTextBox" Grid.Row="1" Grid.ColumnSpan="3" Margin="20,0,20,0"/>
                <TextBlock Grid.Row="2" Grid.ColumnSpan="3" Text="Password" FontSize="24" Margin="20,0,20,0" />
                <PasswordBox Name="PasswordTextBox" Grid.Row="3" Grid.ColumnSpan="3" Margin="20,0,20,0"/>
    
                <Button Grid.Row="4" Grid.ColumnSpan="3" HorizontalAlignment="Center" VerticalAlignment="Center"
                            Content="1. Login and register" Click="LoginAndRegisterClick" Margin="0,0,0,20"/>
    
                <ToggleButton Name="toggleWNS" Grid.Row="5" Grid.Column="0" HorizontalAlignment="Right" Content="WNS" IsChecked="True" />
                <ToggleButton Name="toggleFCM" Grid.Row="5" Grid.Column="1" HorizontalAlignment="Center" Content="FCM" />
                <ToggleButton Name="toggleAPNS" Grid.Row="5" Grid.Column="2" HorizontalAlignment="Left" Content="APNS" />
    
                <TextBlock Grid.Row="6" Grid.ColumnSpan="3" Text="Username Tag To Send To" FontSize="24" Margin="20,0,20,0"/>
                <TextBox Name="ToUserTagTextBox" Grid.Row="7" Grid.ColumnSpan="3" Margin="20,0,20,0" TextWrapping="Wrap" />
                <TextBlock Grid.Row="8" Grid.ColumnSpan="3" Text="Enter Notification Message" FontSize="24" Margin="20,0,20,0"/>
                <TextBox Name="NotificationMessageTextBox" Grid.Row="9" Grid.ColumnSpan="3" Margin="20,0,20,0" TextWrapping="Wrap" />
                <Button Grid.Row="10" Grid.ColumnSpan="3" HorizontalAlignment="Center" Content="2. Send push" Click="PushClick" Name="SendPushButton" />
            </Grid>
        </StackPanel>
    </Grid>
    
  9. 在 [方案總管] 中,開啟 [(Windows 8.1)] 和 [(Windows Phone 8.1)] 專案的 MainPage.xaml.cs 檔案。In Solution Explorer, open the MainPage.xaml.cs file for the (Windows 8.1) and (Windows Phone 8.1) projects. 在這兩個檔案頂端加入下列 using 陳述式:Add the following using statements at the top of both files:

    using System.Net.Http;
    using Windows.Storage;
    using System.Net.Http.Headers;
    using Windows.Networking.PushNotifications;
    using Windows.UI.Popups;
    using System.Threading.Tasks;
    
  10. WindowsAppMainPage 專案的 MainPage.xaml.cs 中,將下列成員新增至 類別。In MainPage.xaml.cs for the WindowsApp project, add the following member to the MainPage class. 請務必將 <Enter Your Backend Endpoint> 取代為您先前取得的實際後端端點。Be sure to replace <Enter Your Backend Endpoint> with your actual backend endpoint obtained previously. 例如: http://mybackend.azurewebsites.netFor example, http://mybackend.azurewebsites.net.

    private static string BACKEND_ENDPOINT = "<Enter Your Backend Endpoint>";
    
  11. 將下面的程式碼新增到 [(Windows 8.1)] 和 [(Windows Phone 8.1)] 專案之 MainPage.xaml.cs 中的 MainPage 類別。Add the code below to the MainPage class in MainPage.xaml.cs for the (Windows 8.1) and (Windows Phone 8.1) projects.

    PushClick 方法是 [傳送推播] 按鈕的 click 處理常式。The PushClick method is the click handler for the Send Push button. 它會呼叫後端以觸發所有裝置的通知,而所有裝置都具有符合 to_tag 參數的使用者名稱標記。It calls the backend to trigger a notification to all devices with a username tag that matches the to_tag parameter. 通知訊息會以要求主體的 JSON 內容形式傳送。The notification message is sent as JSON content in the request body.

    LoginAndRegisterClick 方法是 [登入並註冊] 按鈕的 click 處理常式。The LoginAndRegisterClick method is the click handler for the Login and register button. 它會在本機儲存體中儲存基本驗證權杖 (代表驗證配置使用的任何權杖),然後使用 RegisterClient 來註冊使用後端的通知。It stores the basic authentication token (represents any token your authentication scheme uses) in local storage, then uses RegisterClient to register for notifications using the backend.

    private async void PushClick(object sender, RoutedEventArgs e)
    {
        if (toggleWNS.IsChecked.Value)
        {
            await sendPush("wns", ToUserTagTextBox.Text, this.NotificationMessageTextBox.Text);
        }
        if (toggleFCM.IsChecked.Value)
        {
            await sendPush("fcm", ToUserTagTextBox.Text, this.NotificationMessageTextBox.Text);
        }
        if (toggleAPNS.IsChecked.Value)
        {
            await sendPush("apns", ToUserTagTextBox.Text, this.NotificationMessageTextBox.Text);
    
        }
    }
    
    private async Task sendPush(string pns, string userTag, string message)
    {
        var POST_URL = BACKEND_ENDPOINT + "/api/notifications?pns=" +
            pns + "&to_tag=" + userTag;
    
        using (var httpClient = new HttpClient())
        {
            var settings = ApplicationData.Current.LocalSettings.Values;
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", (string)settings["AuthenticationToken"]);
    
            try
            {
                await httpClient.PostAsync(POST_URL, new StringContent("\"" + message + "\"",
                    System.Text.Encoding.UTF8, "application/json"));
            }
            catch (Exception ex)
            {
                MessageDialog alert = new MessageDialog(ex.Message, "Failed to send " + pns + " message");
                alert.ShowAsync();
            }
        }
    }
    
    private async void LoginAndRegisterClick(object sender, RoutedEventArgs e)
    {
        SetAuthenticationTokenInLocalStorage();
    
        var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
    
        // The "username:<user name>" tag gets automatically added by the message handler in the backend.
        // The tag passed here can be whatever other tags you may want to use.
        try
        {
            // The device handle used is different depending on the device and PNS.
            // Windows devices use the channel uri as the PNS handle.
            await new RegisterClient(BACKEND_ENDPOINT).RegisterAsync(channel.Uri, new string[] { "myTag" });
    
            var dialog = new MessageDialog("Registered as: " + UsernameTextBox.Text);
            dialog.Commands.Add(new UICommand("OK"));
            await dialog.ShowAsync();
            SendPushButton.IsEnabled = true;
        }
        catch (Exception ex)
        {
            MessageDialog alert = new MessageDialog(ex.Message, "Failed to register with RegisterClient");
            alert.ShowAsync();
        }
    }
    
    private void SetAuthenticationTokenInLocalStorage()
    {
        string username = UsernameTextBox.Text;
        string password = PasswordTextBox.Password;
    
        var token = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(username + ":" + password));
        ApplicationData.Current.LocalSettings.Values["AuthenticationToken"] = token;
    }
    
  12. 開啟 App.xaml.cs 並且在 OnLaunched() 事件處理常式中尋找對 InitNotificationsAsync() 的呼叫。Open App.xaml.cs and find the call to InitNotificationsAsync() in the OnLaunched() event handler. 取消註解或刪除對 InitNotificationsAsync()的呼叫。Comment out or delete the call to InitNotificationsAsync(). 此按鈕處理常式會初始化通知註冊。The button handler initializes notification registrations.

    protected override void OnLaunched(LaunchActivatedEventArgs e)
    {
        //InitNotificationsAsync();
    
  13. 以滑鼠右鍵按一下 WindowsApp 專案、按一下 [新增] ,然後按一下 [類別] 。Right-click the WindowsApp project, click Add, and then click Class. 將類別命名為 RegisterClient.cs,然後按一下 [確定] 以產生類別。Name the class RegisterClient.cs, then click OK to generate the class.

    為了註冊推播通知,此類別會包裝連絡應用程式後端所需的 REST 呼叫。This class wraps the REST calls required to contact the app backend, in order to register for push notifications. 它也會在本機儲存通知中心所建立的 registrationIds ,如 從您的應用程式後端註冊中的詳細說明。It also locally stores the registrationIds created by the Notification Hub as detailed in Registering from your app backend. 當您按一下 [登入並註冊] 按鈕時,系統會使用儲存在本機儲存體中的授權權杖。It uses an authorization token stored in local storage when you click the Login and register button.

  14. 在 RegisterClient.cs 檔案開頭加入下列 using 陳述式:Add the following using statements at the top of the RegisterClient.cs file:

    using Windows.Storage;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using Newtonsoft.Json;
    using System.Threading.Tasks;
    using System.Linq;
    
  15. RegisterClient 類別定義中加入下列程式碼。Add the following code inside the RegisterClient class definition.

    private string POST_URL;
    
    private class DeviceRegistration
    {
        public string Platform { get; set; }
        public string Handle { get; set; }
        public string[] Tags { get; set; }
    }
    
    public RegisterClient(string backendEndpoint)
    {
        POST_URL = backendEndpoint + "/api/register";
    }
    
    public async Task RegisterAsync(string handle, IEnumerable<string> tags)
    {
        var regId = await RetrieveRegistrationIdOrRequestNewOneAsync();
    
        var deviceRegistration = new DeviceRegistration
        {
            Platform = "wns",
            Handle = handle,
            Tags = tags.ToArray<string>()
        };
    
        var statusCode = await UpdateRegistrationAsync(regId, deviceRegistration);
    
        if (statusCode == HttpStatusCode.Gone)
        {
            // regId is expired, deleting from local storage & recreating
            var settings = ApplicationData.Current.LocalSettings.Values;
            settings.Remove("__NHRegistrationId");
            regId = await RetrieveRegistrationIdOrRequestNewOneAsync();
            statusCode = await UpdateRegistrationAsync(regId, deviceRegistration);
        }
    
        if (statusCode != HttpStatusCode.Accepted && statusCode != HttpStatusCode.OK)
        {
            // log or throw
            throw new System.Net.WebException(statusCode.ToString());
        }
    }
    
    private async Task<HttpStatusCode> UpdateRegistrationAsync(string regId, DeviceRegistration deviceRegistration)
    {
        using (var httpClient = new HttpClient())
        {
            var settings = ApplicationData.Current.LocalSettings.Values;
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", (string) settings["AuthenticationToken"]);
    
            var putUri = POST_URL + "/" + regId;
    
            string json = JsonConvert.SerializeObject(deviceRegistration);
                            var response = await httpClient.PutAsync(putUri, new StringContent(json, Encoding.UTF8, "application/json"));
            return response.StatusCode;
        }
    }
    
    private async Task<string> RetrieveRegistrationIdOrRequestNewOneAsync()
    {
        var settings = ApplicationData.Current.LocalSettings.Values;
        if (!settings.ContainsKey("__NHRegistrationId"))
        {
            using (var httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", (string)settings["AuthenticationToken"]);
    
                var response = await httpClient.PostAsync(POST_URL, new StringContent(""));
                if (response.IsSuccessStatusCode)
                {
                    string regId = await response.Content.ReadAsStringAsync();
                    regId = regId.Substring(1, regId.Length - 2);
                    settings.Add("__NHRegistrationId", regId);
                }
                else
                {
                    throw new System.Net.WebException(response.StatusCode.ToString());
                }
            }
        }
        return (string)settings["__NHRegistrationId"];
    
    }
    
  16. 儲存您的所有變更。Save all your changes.

測試應用程式Test the Application

  1. 在這兩種 Windows 系統上啟動應用程式。Launch the application on both Windows.

  2. 輸入 [使用者名稱] 和 [密碼] ,如下列畫面所示。Enter a Username and Password as shown in the screen below. 它應該與您在 Windows Phone 上輸入的使用者名稱和密碼不同。It should differ from the user name and password you enter on Windows Phone.

  3. 按一下 [登入並註冊] ,並確認顯示您已登入的對話方塊。Click Log in and register and verify a dialog shows that you have logged in. 此程式碼也會啟用 [傳送推播] 按鈕。This code also enables the Send Push button.

  4. 然後,在 [收件者使用者名稱標記] 欄位中,輸入已註冊的使用者名稱。Then in the Recipient Username Tag field, enter the user name registered. 輸入通知訊息,然後按一下 [傳送推播] 。Enter a notification message and click Send Push.

  5. 只有已經使用相符使用者名稱標記所註冊的裝置才會收到通知訊息。Only the devices that have registered with the matching username tag receive the notification message.

後續步驟Next steps

在本教學課程中,您已學會如何針對具有與其註冊相關聯標記的使用者,將通知推送至這些特定使用者。In this tutorial, you learned how to push notifications to specific users that have tags associated with their registrations. 若要了解如何推送以位置為基礎的通知,請繼續進行下列教學課程:To learn how to push location-based notifications, advance to the following tutorial: