Push notification support in Azure enables you to access an easy-to-use, multiplatform, and scaled-out push infrastructure, which greatly simplifies the implementation of push notifications for both consumer and enterprise applications for mobile platforms. This tutorial shows you how to use Azure Notification Hubs to send push notifications to a specific app user on a specific device. An ASP.NET WebAPI backend is used to authenticate clients and to generate notifications, as shown in the guidance topic Registering from your app backend. This tutorial builds on the notification hub that you created in the Get started with Notification Hubs tutorial.
This tutorial is also the prerequisite to the Secure Push tutorial. After you have completed the steps in this Notify Userstutorial, you can proceed to the Secure Push tutorial, which shows how to modify the Notify Users code to send a push notification securely.
NOTE:
This tutorial assumes that you have created and configured your notification hub as described in Getting Started with Notification Hubs (Windows Store). Also note that in this tutorial you will create a Windows Phone 8.1 app. The same code can be used for Windows Store and Windows Universal apps. All of these apps have to use Windows (not Windows Phone) credentials.
Create and Configure the Notification Hub
Before you begin this tutorial, you must reserve an application name, then create an Azure Notification Hub and connect it to that application. Please follow the steps in Getting Started with Notification Hubs (Windows Store); specifically, the sectionsRegister your app for the Windows Store and Configure your Notification Hub. In particular, make sure that you have entered the Package SID and Client Secret values in the portal, in the Configure tab for your notification hub. This configuration procedure is described in the section Configure your Notification Hub. This is an important step: if the credentials on the portal do not match those specified for the app name you choose, the push notification will not succeed.
Create the WebAPI Project
Follow the steps below to create a new ASP.NET WebAPI backend to authenticate clients and generate notifications, or modify an existing backend from previous projects or the Send push notifications to authenticated users tutorial.
NOTE:
Important: Before starting this tutorial, please ensure that you have installed the latest version of the NuGet Package Manager. To check, start Visual Studio. From the Tools menu, click Extensions and Updates. Search forNuGet Package Manager for Visual Studio 2013, and make sure you have version 2.8.50313.46 or later. If not, please uninstall, then reinstall the NuGet Package Manager.
[AZURE.NOTE] Make sure you have installed the Visual Studio Azure SDK for website deployment.
- Start Visual Studio or Visual Studio Express.
- In Visual Studio, click File, then click New, then Project, expand Templates, Visual C#, then click Web and ASP.NET Web Application, type the name AppBackend, and then click OK.
- In the New ASP.NET Project dialog, click Web API, then click OK.
- In the Configure Azure Site dialog, choose a subscription, region, and database to use for this project. Then click OK to create the project.
- In Solution Explorer, right-click the AppBackend project and then click Manage NuGet Packages.
- On the left-hand side, click Online, and search for servicebus in the Search box.
- In the results list, click Windows Azure Service Bus, and then click Install. Complete the installation, then close the NuGet package manager window.
- We will now create a new class Notifications.cs. Go to the Solution Explorer, right-click the Models folder, click Add, thenClass. After naming the new class Notifications.cs, click Add to generate the class. This module represents the different secure notifications that will be sent. In a complete implementation, the notifications are stored in a database. For simplicity, this tutorial stores them in memory.
- In Notifications.cs, add the following
using
statement at the top of the file:using Microsoft.ServiceBus.Notifications;
- Then replace the
Notifications
class definition with the following and make sure to replace the two placeholders with the connection string (with full access) for your notification hub, and the hub name (available at Azure Management Portal):public class Notifications { public static Notifications Instance = new Notifications(); public NotificationHubClient Hub { get; set; } private Notifications() { Hub = NotificationHubClient.CreateClientFromConnectionString("{conn string with full access}", "{hub name}"); } }
- We will then create a new class AuthenticationTestHandler.cs. In Solution Explorer, right-click the AppBackend project, click Add, then click Class. Name the new class AuthenticationTestHandler.cs, and click Add to generate the class. This class is used to authenticate users using Basic Authentication. Note that your app can use any authentication scheme.
- In AuthenticationTestHandler.cs, add the following
using
statements:using System.Net.Http; using System.Threading.Tasks; using System.Threading; using System.Text; using System.Security.Principal; using System.Net;
- In AuthenticationTestHandler.cs, replacing the
AuthenticationTestHandler
class definition with the following: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 Unauthorised(); } else return Unauthorised(); 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> Unauthorised() { var response = new HttpResponseMessage(HttpStatusCode.Forbidden); var tsc = new TaskCompletionSource<HttpResponseMessage>(); tsc.SetResult(response); return tsc.Task; } }
- Add the following code at the end of the
Register
method in the App_Start/WebApiConfig.cs class:config.MessageHandlers.Add(new AuthenticationTestHandler());
- Next we create a new controller RegisterController. In Solution Explorer, right-click the Controllers folder, then click Add, then click Controller. Click the Web API 2 Controller -- Empty item, and then click Add. Name the new classRegisterController, and then click Add again to generate the controller.
- In RegiterController.cs, add the following
using
statements:using Microsoft.ServiceBus.Notifications; using AppBackend.Models; using System.Threading.Tasks; using Microsoft.ServiceBus.Messaging; using System.Web;
- Add the following code inside the
RegisterController
class definition. Note that in this code, we add the user tag for the user that has been authenticated by the handler. 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) { // make sure there are no existing registrations for this push handle (used for iOS and Android) string newRegistrationId = null; 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 "gcm": registration = new GcmRegistrationDescription(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()); } }
- Create a new controller NotificationsController, following how we created RegisterController. This component exposes a way for the device to retrieve the notification securely, and provides a way for a user to trigger a secure push to devices. Note that when sending the notification to the Notification Hub, we send a raw notification with only the ID of the notification (no actual message).
- In NotificationsController.cs, add the following
using
statements:using AppBackend.Models; using System.Threading.Tasks; using System.Web;
- Add the following code inside the NotificationsController class definition and make sure to comment out the snippets for platforms you are not working with.
public async Task<HttpResponseMessage> Post() { var user = HttpContext.Current.User.Identity.Name; var userTag = "username:"+user; // windows var toast = @"<toast><visual><binding template=""ToastText01""><text id=""1"">Hello, " + user + "</text></binding></visual></toast>"; await Notifications.Instance.Hub.SendWindowsNativeNotificationAsync(toast, userTag); // apns var alert = "{\"aps\":{\"alert\":\"Hello\"}}"; await Notifications.Instance.Hub.SendAppleNativeNotificationAsync(alert, userTag); // gcm var notif = "{ \"data\" : {\"msg\":\"Hello\"}}"; await Notifications.Instance.Hub.SendGcmNativeNotificationAsync(notif, userTag); return Request.CreateResponse(HttpStatusCode.OK); }
- Press F5 to run the application and to ensure the accuracy of your work so far. The app should launch a web browser and display the ASP.NET home page.
- Now we will deploy this app to an Azure Website in order to make it accessible from all devices. Right-click on theAppBackend project and select Publish.
- Select Azure Website as your publish target.
- Log in with your Azure account and select an existing or new Website.
- Make a note of the destination URL property in the Connection tab. We will refer to this URL as your backend endpointlater in this tutorial. Click Publish.
Create the Windows Phone Project
The next step is to create the Windows Phone application. To add this project to the current solution, do the following:
- In Solution Explorer, right-click the top-level node of the solution (Solution NotifyUsers in this case), then click Add, then click New Project.
- Expand Store Apps, then click Windows Phone Apps, then click Blank App (Windows Phone).
- In the Name box, type NotifyUserWindowsPhone, then click OK to generate the project.
- Associate this application with the Windows Phone Store: in Solution Explorer, right-click NotifyUserWindowsPhone (Windows Phone 8.1), then click Store, and then click Associate App with the Store....
- Follow the steps in the wizard to sign in and associate the app with the Store.
NOTE:
Be sure to make a note of the name of the application you choose during this procedure. You must configure the notification hub on the portal using the credentials you obtain from the Windows Dev Center for this specific reserved app name. This configuration procedure is described in Configure your Notification Hub. This is an important step: if the credentials on the portal do not match those specified for the app name you choose, the push notification will not succeed. - In Solution Explorer, right-click the NotifyUserWindowsPhone (Windows Phone 8.1) project and then click Manage NuGet Packages.
- On the left-hand side, click Online.
- In the Search box, type Http Client.
- In the results list, click Microsoft HTTP Client Libraries, and then click Install. Complete the installation.
- Back in the NuGet Search box, type Json.net. Install the Json.NET package, and then close the NuGet Package Manager window.
- In Solution Explorer, in the NotifyUserWindowsPhone (Windows Phone 8.1) project, double-click MainPage.xaml to open it in the Visual Studio editor.
- In the MainPage.xaml XML code, replace the
<Grid>
section with the following code:<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="Secure Push" 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="*"/> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="Username" FontSize="24" Margin="20,0,20,0"/> <TextBox Name="UsernameTextBox" Grid.Row="1" Margin="20,0,20,0"/> <TextBlock Grid.Row="2" Text="Password" FontSize="24" Margin="20,0,20,0" /> <PasswordBox Name="PasswordTextBox" Grid.Row="3" Margin="20,0,20,0"/> <Button Grid.Row="4" HorizontalAlignment="Center" VerticalAlignment="Center" Content="1. Login and register" Click="LoginAndRegisterClick" /> <Button Grid.Row="5" HorizontalAlignment="Center" VerticalAlignment="Center" Content="2. Send push" Click="PushClick" /> </Grid> </StackPanel> </Grid>
- In Solution Explorer, right-click the NotifyUserWindowsPhone (Windows Phone 8.1) project, then click Add, and then click Class. Name the class RegisterClient.cs, then click OK to generate the class. This component implements the REST calls required to contact the app backend, in order to register for push notifications. It also locally stores theregistrationIds created by the Notification Hub as detailed in Registering from your app backend. Note that it uses an authorization token stored in local storage when you click the Log in and register button.
- Add the following code inside the
RegisterClient
class definition. Be sure to replace{backend endpoint}
with the your backend endpoint obtained in the previous section:private string POST_URL = "{backend endpoint}/api/register"; private class DeviceRegistration { public string Platform { get; set; } public string Handle { get; set; } public string[] Tags { get; set; } } 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) { // log or throw } } 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 Exception(); } } } return (string)settings["__NHRegistrationId"]; }
- 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;
- Add code for buttons in MainPage.xaml.cs. The callback for Log in and register stores the basic authentication token in local storage (note that this represents any token your authentication scheme uses), then uses
RegisterClient
to call the backend. The callback for AppBackend calls the backend to trigger a secure notification to all devices of this user.Add the following code to MainPage.xaml.cs after theOnNavigatedTo()
method. Be sure to replace{backend endpoint}
with the backend endpoint obtained in the previous section:private async void PushClick(object sender, RoutedEventArgs e) { var POST_URL = "{backend endpoint}/api/notifications"; using (var httpClient = new HttpClient()) { var settings = ApplicationData.Current.LocalSettings.Values; httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", (string) settings["AuthenticationToken"]); await httpClient.PostAsync(POST_URL, new StringContent("")); } } private async void LoginAndRegisterClick(object sender, RoutedEventArgs e) { SetAuthenticationTokenInLocalStorage(); var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync(); await new RegisterClient().RegisterAsync(channel.Uri, new string[] { "myTag" }); var dialog = new MessageDialog("Registered as: " + UsernameTextBox.Text); dialog.Commands.Add(new UICommand("OK")); await dialog.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; }
- Add the following
using
statements at the top of the MainPage.xaml.cs file:using System.Net.Http; using Windows.Storage; using System.Net.Http.Headers; using Windows.Networking.PushNotifications; using Windows.UI.Popups;
Run the Application
To run the application, do the following:
- In Visual Studio, run the NotifyUserWindowsPhone (Windows Phone 8.1) Windows Phone app. The Windows Phone emulator runs and loads the app automatically.
- In the NotifyUserWindowsPhone app UI, enter a username and password. These can be any string, but they must be the same value.
- In the NotifyUserWindowsPhone app UI, click Log in and register. Then click Send push.
Source from
http://azure.microsoft.com/en-us/documentation/articles/notification-hubs-aspnet-backend-windows-dotnet-notify-users/
No comments :
Post a Comment