Monday 24 February 2014

Windows Azure Mobile Services - Web API - Push Notifications

Node.js services have the option of manually managing device push channels (through your own channel registration table) and directly pushing to a devices URI handle or using the Notifications hub to take care of the devices for you (I notice that the Node.js services still have this, but also have an 'ENABLE ENHANCED PUSH' button in the portal to automatically integrate a notifications hub).

Web API services don't have the option to make direct platform-specific push requests and by default come with a Notification Hub instance created and ready to use.

Last year I wrote an article about using the Notifications Hub in Node.js. The implementation is very similar in Web API. This article explains how to integrate the hub into your applications.

Here is an implementation of a scheduled job which is used to create a level board for a fictitious game, it calls two methods which send toast and tile notifications for Windows Phone MPNS via the hub:

using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.WindowsAzure.Mobile.Service;
using System;

namespace TileTapperWebAPIService.ScheduledJobs
{
    public class LevelJob : ScheduledJob
    {
        public override async Task ExecuteAsync()
        {
            // Level name
            string levelName = string.Format("Daily Level {0}", DateTime.Now.ToShortDateString());

            // Logic to create level ommited
            //
            //

            string title = "New Level!";

            await this.SendToastMpns(title, levelName);
            await this.SendTileMpns(title, levelName);

            Services.Log.Info(string.Format("{0} - Created", levelName));
        }

        private async Task SendToastMpns(string text1, string text2)
        {
            try
            {
                var toast = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<wp:Notification xmlns:wp=\"WPNotification\">" +
                    "<wp:Toast>" +
                        "<wp:Text1>" + text1 + "</wp:Text1>" +
                        "<wp:Text2>" + text2 + "</wp:Text2>" +
                    "</wp:Toast> " +
                "</wp:Notification>";

                await base.Services.Push.HubClient.SendMpnsNativeNotificationAsync(toast);
            }
            catch (Exception ex)
            {
                base.Services.Log.Error(ex);
            }
        }

        private async Task SendTileMpns(string backTitle, string backContent)
        {
            try
            {
                var tile = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<wp:Notification xmlns:wp=\"WPNotification\" Version=\"2.0\">" +
                    "<wp:Tile Template=\"FlipTile\">" +
                        "<wp:BackTitle>" + backTitle + "</wp:BackTitle>" +
                        "<wp:BackContent>" + backContent + "</wp:BackContent>" +
                        "<wp:WideBackContent>" + backContent + "</wp:WideBackContent>" +
                    "</wp:Tile> " +
                "</wp:Notification>";

                await base.Services.Push.HubClient.SendMpnsNativeNotificationAsync(tile);
            }
            catch (Exception ex)
            {
                base.Services.Log.Error(ex);
            }
        }
    }

}

We can configure this in the 'SCHEDULER' tab of the portal and run on a timed schedule or on demand, or make an HTTP POST request to call it like this:

Request:
POST https://tiletapperwebapi.azure-mobile.net/jobs/level HTTP/1.1
x-zumo-master: XXXXXXXXXXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Host: tiletapperwebapi.azure-mobile.net
Content-Length: 0

Response:
HTTP/1.1 200 OK
Content-Length: 0
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=dd8538851a2df7249d42a39ee57c8edadae2824386ae2790373823ba2f34a746;Path=/;Domain=tiletapperwebapi.azure-mobile.net
Set-Cookie: WAWebSiteSID=71043e6c932a454d8c4cf9d17a9ed735; Path=/; HttpOnly
Date: Mon, 24 Feb 2014 10:59:04 GMT


2 comments:

  1. WTF no client code? What is the use of the feature if I can't use it???

    ReplyDelete
  2. If you read the intro, there's a link to a previous article with node backend which has the client code ;)

    ReplyDelete