Partager via


Push Notifications Using Notification Hub and .NET Backend

When creating a Azure Mobile Service, a Notification Hub is automatically created as well enabling large scale push notifications to devices across any mobile platform (Android, iOS, Windows Store apps, and Windows Phone). For a background on Notification Hubs, see this overview as well as these tutorials and guides, and Scott Guthrie’s blog Broadcast push notifications to millions of mobile devices using Windows Azure Notification Hubs.

Let’s look at how devices register for notification and how to send notifications to registered devices using the .NET backend.

Notification Hub Creation

When creating an Azure Mobile Service, a Service Bus Notification Hub is automatically provisioned for that service. A Notification Hub is specified by two parameters: a Service Bus Connection string and the name of the Notification Hub which both are derived from the service name. You can inspect the state of the Notification Hub in the Azure Portal by looking under the Service Bus tab, for example, for the Mobile Service henrikntest11, it looks like this:

ServiceBusNotificationHub

Note that a Notification Hub is not deleted when a Mobile Service is deleted – that is, it is created with the Service but deleting it is handled independently from the Mobile Service. This is so that you won’t loose notification hub registrations even if you delete the Mobile Service.

Note: We are working on exposing connection strings in the portal so that you can point a Mobile Service at any Notification Hub – if you want to do this now then you’ll have to do it as part of the startup code for the service.

Device Registration, Templates, and Tags

In order to send push notifications to a device, the device first has to register for notifications. A device registration contains information about how a device would like to get notified and what it is interested in getting notifications for. The registration process looks like this:

NotificationRegistration

First the device gets a new registration ID by issuing a POST request against the Registration ID controller. With the registration ID in hand, the device can either PUT, GET, or DELETE a registration against the Registration controller. The registration contains information about how the device wants to receive push notifications. For example, it can request “native” notifications using the particular format of each push service such as Apple Push Notification Service (APNS), Google Cloud Messaging for Chrome (GCM), Microsoft Push Notification Services (MPNS), or Windows Push Notification Services (WNS). Alternatively it can request template notifications which allow for personalization and customization across any of the push platforms.

Registration templates are great in that each device controls the shape of the notifications it receives hence making it a lot easier for the sender not only to send notifications across client platforms but also to personalize them to a particular user and device. The template contains a simple expression language so instead of the sender sending the entire notification, it just sends a set of keywords that are filled in at the places of the expressions in the templates, and voila, the Notification Hub creates a fully customized notification targeting a particular device and user. The template documentation has a great description of how templates work including this illustration of how the keywords can be used to generate personalized notifications across platforms:

NotificationTemplates

The second part of the registration is a set of tags which indicates when a device or user wants to receive a notification. The routing and tag documentation provides a good description including this illustration of how tags are used to route and filter notifications:

NotificationTags

Extending the Registration Process

Using the .NET backend it is possible to inject code into the registration process allowing you to manipulate the registration before it is sent to the Notification Hub. For example, you can add, remove, or change which tags are associated with a particular registration, or change the template used for registration. To inject code, simply implement the INotificationHandler interface which allows for injection of code both into the registration and unregistration process, for example:

    1: /// <summary>
    2: /// Allows for injection of custom code into the notification hub registration process.
    3: /// All public, non-abstract implementations of this interface will get picked up automatically by 
    4: /// the default <see cref="INotificationHandlerTypeResolver"/> implementation and registered with
    5: /// the dependency engine, causing then to get called as part of the registration process.
    6: /// </summary>
    7: public class MyNotificationHandler : INotificationHandler
    8: {
    9:     /// <summary>
   10:     /// This method is called when a client is registering to receive notifications. It is called 
   11:     /// just before the request is submitted to the Notification Hub and so the implementation of 
   12:     /// this method can modify the <paramref name="registration"/> before it is submitted.
   13:     /// </summary>
   14:     public Task Register(ApiServices services, HttpRequestContext context, NotificationRegistration registration)
   15:     {
   16:         registration.Tags.Add("Henrik");
   17:         return Task.FromResult(true);
   18:     }
   19:  
   20:     /// <summary>
   21:     /// This method is called when a client is unregistering to no longer receive notifications. It is called 
   22:     /// just before the request is submitted to the Notification Hub.
   23:     /// </summary>
   24:     public Task Unregister(ApiServices services, HttpRequestContext context, string deviceId)
   25:     {
   26:         return Task.FromResult(true);
   27:     }
   28: }

An INotificationHandler implementation can also cancel a registration or unregistration by throwing an exception. If the exception is an HttpResponseException, then the HttpResponseMessage included will be used as the response sent to the client.

It is possible to provide multiple INotificationHandler implementations which will cause them all to run as part of device registration and unregistration.

Authorizing Clients to Register for Notifications

The default authorization level required for clients to register is application level which simply means that the application needs to use the public application key. However, you can change this setting using the ConfigOptions class during startup. This example shows how to set it to user level:

    1: public static class WebApiConfig
    2: {
    3:     public static void Register()
    4:     {
    5:         ConfigOptions options = new ConfigOptions() 
    6:         { 
    7:             PushAuthorization = AuthorizationLevel.User 
    8:         };
    9:         HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));
   10:  
   11:         // Any additional initialization
   12:     }
   13: }

Note that this just makes the registration operation available only to authenticated users. If you want to ensure that a user can subscribe only to a certain set of tags (e.g. Alice cannot register the tag for Bob’s userid), you have to create an INotificationHandler instance that performs that check as explained in the previous section.

Sending Push Notifications

The .NET backend provides the PushClient class for sending messages using either APNS, GCM, WNS, MPNS, or template-based push notifications. The PushClient is accessible from the ApiServices class which is injected automatically into ApiController classes and ScheduledJobs allowing you send push notifications from pretty much anywhere. Let’s first assume you have a simple ApiController which gets the ApiServices instance through property injection (you can also use constructor injection if you prefer):

    1: public class CustomController : ApiController
    2: {
    3:     public ApiServices Services { get; set; }
    4:  
    5:     public async Task Post(string input)
    6:     {
    7:         // Send push notification
    8:     }
    9: }

With this, here’s what sending messages from the POST handler using the various platforms look like:

Sending Template Messages

Sending template messages is by far the simplest way of sending push notifications as it only involves sending a dictionary of keywords to be inserted into the templates provided by the client during registration. Here’s an example of sending a template message:

    1: TemplatePushMessage message = new TemplatePushMessage()
    2: {
    3:     { "tempC", "24.0"},
    4:     { "tempF", "75.2"},
    5: };
    6: await Services.Push.SendAsync(message);

Sending APNS Messages

APNS messages can include a variety of parameters (see Apple Push Notification Service for a detailed description). Here’s an example of sending a simple APNS notification message with an expiration of 1 hour:

    1: ApplePushMessage message = new ApplePushMessage("Hello world!", TimeSpan.FromHours(1));
    2: await Services.Push.SendAsync(message);

It is also possible to generate a complex APNS message with additional properties:

    1: ApplePushMessage message = new ApplePushMessage();
    2: message.Aps.AlertProperties.Body = "Hello world!";
    3: message.Aps.AlertProperties.ActionLocKey = "Greeting";
    4: message.Aps.Badge = 5;
    5: message["myKey1"] = "myValue1";
    6: message["myKey2"] = new Collection<string> { "keyA", "valueA" };
    7: await Services.Push.SendAsync(message);

Or, if you want complete control over the serialized format, you can set the payload directly:

    1: ApplePushMessage message = new ApplePushMessage();
    2: message.JsonPayload = "{ \"aps\" : { \"alert\" : \"Hello World!\" } }";
    3: await Services.Push.SendAsync(message);

Sending CGM Messages

Sending GCM messages (see Google Cloud Messaging for Android for details) is similar to sending APNS messages. For example, you can send a simple message with a 1 hour expiration like this:

    1: Dictionary<string, string> data = new Dictionary<string, string>()
    2: {
    3:     { "greeting", "Hello World!" }
    4: };
    5: GooglePushMessage message = new GooglePushMessage(data, TimeSpan.FromHours(1));
    6: await Services.Push.SendAsync(message);

You can also generate a complex message with whatever properties you like:

    1: GooglePushMessage message = new GooglePushMessage();
    2: message.Data.Add("greeting", "Hello World!");
    3: message.DelayWhileIdle = true;
    4: message.TimeToLiveInSeconds = 60 * 60 * 2;
    5: message.Add("myKey1", "myValue");
    6: await Services.Push.SendAsync(message);

And if you want complete control over the serialized format then you can provide the payload directly:

    1: GooglePushMessage message = new GooglePushMessage();
    2: message.JsonPayload = "{ \"registration_id\" : \"APA9...\", \"data\" : { \"greeting\" : \"Hello World!\" } }";
    3: await Services.Push.SendAsync(message);

Sending WNS Messages

WNS messages have a wide variety of styles that can be used (see The tile template catalog for more details). The WindowsPushMessage class can help you create a particular tile – for example, this will send a TileSquare150x150Block tile

    1: TileBinding binding = new TileBinding(new TileText { Id = 1, Text = "42" },  new TileText { Id = 2, Text = "Hello World!" })
    2: {
    3:     Template = "TileSquare150x150Block",
    4:     Fallback = "TileSquareBlock"                 
    5: };
    6: WindowsPushMessage message = new WindowsPushMessage(2, binding);
    7: await Services.Push.SendAsync(message);

As for the other platforms, you can also provide a pre-built payload (this time in XML) which will be sent as-is:

    1: WindowsPushMessage message = new WindowsPushMessage();
    2: message.XmlPayload = "<tile> <visual version=\"2\"> <binding template=\"TileSquare150x150Block\" fallback=\"TileSquareBlock\"> <text id=\"1\">42</text> <text id=\"2\">Hello World!</text> </binding> </visual> </tile>";
    3: await Services.Push.SendAsync(message);

Sending MPNS Message

Lastly, you can send MPNS messages (see Tiles and notifications for Windows Phone 8 for details) using the MpnsPushMessage class, for example like this:

    1: Toast toast = new Toast()
    2: {
    3:      Text1 = "Greeting",
    4:      Text2 = "Hello World!"
    5: };
    6: MpnsPushMessage message = new MpnsPushMessage(toast);
    7: await Services.Push.SendAsync(message);

In addition to toasts, you can send FlipTile, IconicTile, or CycleTile, and again, you can also set the raw payload (in XML) which will then be sent as is.

Have fun!

Henrik

Comments

  • Anonymous
    May 23, 2014
    Hi Henrik - do you have some code to demonstrate what you blogged? it is interesting the way you use the notification hub. I would love see how you actually done it. Thanks
  • Anonymous
    June 04, 2014
    In the "Field Engineer" sample (code.msdn.microsoft.com/Field-Engineer-501df99d) you can find one instance where the service is sending a notification using the hub to its clients (more details in this file: code.msdn.microsoft.com/.../sourcecode)
  • Anonymous
    October 02, 2014
    Re: Extending the Registration ProcessI need to review registration requests before they are actioned, so your code snippet is a great help thanks.But,How and where do you implement the handler (public class MyNotificationHandler : INotificationHandler) ?I have searched, but have been unable to find information on how to register the handlers.Thanks
  • Anonymous
    January 21, 2015
    An example without Azure. www.ingens-networks.com/.../Push-Notifications-Hub-Server.aspx
  • Anonymous
    February 18, 2015
    What is the reliability of ANH and of WNS?i.e. if i want to send push notifications to 200-300 millions of windows users, what percentage of the messages are expected to arrive?