Universal App - 整合 Windows Notification Service (WNS) for Server

Universal App - 整合 Windows Notification Service (WNS) for Server

過去開發 Windows Phone 8.0 時使用推播的服務為:Microsoft Push Notification Service,我也有寫過相關的操作:

<Windows Phone 7 – Push Notification基本概念詳述 - 1>與<Windows Phone 7 – Push Notification基本概念詳述 – 2>。

但現在已全部改為 Windows Notification Service 來負責,如果您自己本身沒有 Server 可以考慮搭配 Azure Mobile Service,

我也有寫過相關的文件:

 

根據<Windows Push Notification Services (WNS) overview (Windows Runtime apps)>介紹 WNS 的運作原理如下圖:

IC618819

App 向 Notification Client Platform 請求 push notification channel,再由 Notification Client Platform 向 WNS 取得

channel (它是一個 URI),最後 App 將得到的 channel 傳送給 Cloud Service (或自己的 Server) 進行保存。

 

由於機制與過去相同所以這裡不多加解釋,那一篇要介紹什麼呢?

1) 如果是自行建立的 Server 要如何保存 App 所送出的 channel;

2) Server 如何藉由 HTTP POST 或 Secure Sockets Layer (SSL) 將 notification payload 發送各種不同的 Notification;

3) Server 如何向 WNS 驗證自己的身份為合法;

 

[實作步驟]

A. 從 Windows Dev Center 取得指定 App 專用 WNS 的 Package Security Identifier (SID) 與 secret key

    要能夠使用 push notification 需要先藉由 windows dev center 註冊一個 windows store app 取得需要的 SID 與 secret key,

    這個 Package Security Identifier (SID) 與 secret key 是一對的,要注意保存它主要用驗證 Server (Cloud Service) 向 WNS 的存取權限。

    每一個 App 有一個專屬的 SID 與 secret key,所以不可以共用於其他的 App。

    詳細可參考<How to authenticate with the Windows Push Notification Service (WNS) (Windows Runtime apps)>。

    其中要注意的是:

  「3. On the Push notifications and Live Connect services info page, select Identifying your app.」取得如下圖的 SID 與 secret key:

    Hh465407.6.1-IdentityElement(en-us,WIN.10)

Step 3: Obtain the credentials for your app」取得如下圖的結果:

    擷取

    上述圖片裡最重要的參數:封裝 SID 與 用戶端密碼,千萬不要外流這二組也是往下要與 WNS 溝通的關鍵。

 

B. Server 利用 SID 與 client secret (用戶端密碼) 向 WNS 註冊(Send the cloud server's credentials to WNS) 取得 access token

     利用 HTTPS authentication request 指定 "application/x-www-for-urlencoded" format 將 SID 與 client secret 送給 WNS 進行驗證。

     根據<Push notification service request and response headers (Windows Runtime apps)>的說明:

     a. Access token request 位置:「https://login.live.com/accesstoken.srf」;

     b. Access token request parameters:

         需要傳遞三個重要的參數(要記得做 URL encode) 在 HTTP Request body 與利用 "application/x-www-form-urlencoded" format:

Parameter Required/Optional Description
grant_type Required Must be set to "client_credentials".
client_id Required Package security identifier (SID) for your cloud service as assigned when you registered your app with the Windows Store.
client_secret Required Secret key for your cloud service as assigned when you registered your app with the Windows Store.
scope Required

Must be set to:

  • Windows: "notify.windows.com"
  • Windows Phone: "notify.windows.com" or "s.notify.live.net"

      c. Access token response and Access token response parameters:

          如果請求驗證成功將會收到 "200 OK" 與 "access token",其結構同<OAuth 2.0 protocol draft>的內容。

          response parameters 如下:

Parameter Required/Optional Description
access_token Required The access token that the cloud service will use when it sends a notification.
token_type Optional Always returned as "bearer".

          如果錯誤的話會對應的 Status code 與 描述,如下:

HTTP response code Description
200 OK The request was successful.
400 Bad Request The authentication failed. See the OAuth draft Request for Comments (RFC) for the response parameters.

           response content 例如:

 HTTP/1.1 200 OK   
 Cache-Control: no-store
 Content-Length: 422
 Content-Type: application/json
 
 {
     "access_token":"EgAcAQMAAAAALYAAY/c+Huwi3Fv4Ck10UrKNmtxRO6Njk2MgA=", 
     "token_type":"bearer"
 }

 

統合 A、B 二大步驟,下方的程式碼說明 Server 端如何向 WNS 註冊取得 access token:

protected void OnRegistWNSClick(object sender, EventArgs e)
{
    String sid = WebConfigurationManager.AppSettings["WNSSSID"];
    String secret = WebConfigurationManager.AppSettings["CLIENTSECRET"];
    // 組合 request 必要的參數
    List<String> param = new List<string>();
    param.Add("grant_type=client_credentials");
    param.Add(String.Format("client_id={0}", HttpUtility.UrlEncode(sid)));
    param.Add(String.Format("client_secret={0}", HttpUtility.UrlEncode(secret)));
    // 使用 notify.windows.com 可同時支援 Windows 與 Windows Phone
    param.Add("scope=notify.windows.com");

 
    String paramStr = String.Join("&", param);
    byte[] postData = Encoding.UTF8.GetBytes(paramStr);

 
    // 藉由 POST 與 特定 content type 送出內容
    HttpWebRequest request = HttpWebRequest.CreateHttp("https://login.live.com/accesstoken.srf");
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = postData.Length;
    request.Method = "POST";
    Stream dataStream = request.GetRequestStream();
    dataStream.Write(postData, 0, postData.Length);
    dataStream.Close();

 
    HttpWebResponse response = request.GetResponse() as HttpWebResponse;
    if (response.StatusCode == HttpStatusCode.OK)
    {
        using (Stream resposneStream = response.GetResponseStream())
        {
            StreamReader reader = new StreamReader(resposneStream);
            string responseFromServer = reader.ReadToEnd();
            // 將收到的 json string 轉換成 物件
            WNSResponseObject wnsObject = new JavaScriptSerializer().Deserialize(responseFromServer, typeof(WNSResponseObject)) as WNSResponseObject;                 

 
            Application.Lock();
            Application["access_token"] = wnsObject.access_token;
            Application.UnLock();
        }
    }
    else
    {
        lblStatus.Text = response.StatusDescription;
    }
}

 
public class WNSResponseObject
{
    public string token_type { get; set; }
    public string access_token { get; set; }
    public int expires_in { get; set; }
}

將 response 的 JSON內容建立成 object  來加以反序列化為物件進行操作。接下來往下說明發送各種不同的 notification。

 

C. 發送 notification request 與接收 response

C-1. Send notification request

        發送通知給 App 所註冊的 notification channel,需透過 SSL 的 HTTP request 並在 HTTP Header 指定 Content-Length,這二個是必要的。

        更要注意:

        〉Content-Type 與 Content-Length 二個 header 需要正確因為它會被送至 App;

        〉以下介紹的 HTTP headers 如果有錯誤將會發送失敗;

必要與選用的 parameters,如下:

        〉Request parameters

Header name Required/Optional Description
Authorization Required Standard HTTP authorization header used to authenticate your notification request.
Your cloud service provides its access token in this header.

根據 OAuth 2.0 authorization method,該參數要符合 bearer tokens。寫法:
Authorization: Bearer <access-token>
要注意「Bearer 」加上 <access-token> 中間有一個空白。
Content-Type Required Standard HTTP authorization header.
For toast, tile, and badge notifications, this header must be set to "text/xml".
For raw notifications, this header must be set to "application/octet-stream".
Content-Length Required Standard HTTP authorization header to denote the size of the request payload.
X-WNS-Type Required Defines the notification type in the payload: tile, toast, badge, or raw.

該參數給予的值代表傳送的類型與 WNS 要怎麼處理,另外當 App 收到 notification 後,client 也會驗證該標籤類型與 payload 是否符合。可用值如下:
Value Description
wns/badge A notification to create a badge overlay on the tile. The Content-Type header included in the notification request must be set to "text/xml".
wns/tile A notification to update the tile content. The Content-Type header included in the notification request must be set to "text/xml".
wns/toast A notification to raise a toast on the client. The Content-Type header included in the notification request must be set to "text/xml".
wns/raw A notification which can contain a custom payload and is delivered directly to the app. The Content-Type header included in the notification request must be set to "application/octet-stream".
X-WNS-Cache-Policy Optional Enables or disables notification caching.
This header applies only to tile, badge, and raw notifications.

可用值:
Value Description
cache Default. Notifications will be cached if the user is offline. This is the default setting for tile and badge notifications.
no-cache
The notification will not be cached if the user is offline. This is the default setting for raw notifications.

上表可得知,(預設)當 client 離線無法收取 notification 時,WNS 會 cache 一個 badge 與 一個 tile notification,而 raw notification 不 cache
但要注意 If notification cycling is enabled for the app, WNS will cache up to five tile notifications.
預設 raw notifications 是不 cache 的,但如果開啟 cache 只會有一個 raw notification 被 cache

這些 cache 的 notifications 可能會因為時效性或其他狀況被丟掉,但未被丟棄的 notification 會在下一次用戶上線時加以送出。
X-WNS-RequestForStatus Optional Requests device status and WNS connection status in the notification response.
指定 response 需要夾帶 device status 與 WNS connection status。
Value Description
true Return the device status and notification status in the response.
false Default. Do not return the device status and notification status.
X-WNS-Tag Optional String used to provide a notification with an identifying label, used for tiles that support the notification queue. This header applies only to tile notifications.

分配一個標記標籤到 notification,該標籤作用於 notification queue 與識別之用,如果 queue 中有相同的 tag 將會由最新的取代已存在的 notification。

〉可用字串不可超過 16 個字元(An alphanumeric string of no more than 16 characters)。
〉Windows Phone Store App 使用了 X-WNS-Tag 可搭配 X-WNS-Group 來同時顯示多個 notification 在
     action center 上。
〉 This header is optional and used only when sending tile notification.
X-WNS-TTL Optional Integer value, expressed in seconds, that specifies the time to live (TTL).

可指定該 notification 的過期時間,單位:秒。以 WNS 收到 notification 之後開始計算。
該參數是選用的,如果沒有設定該 notification 不會過期,仍會被新進的 notification 取代。
X-WNS-SuppressPopup Optional Windows Phone only: Delivers a toast notification directly to the action center without raising the toast itself.

指定 notification 抵達 client 時是否要有跳出 toast 或是直接進入 action center。
Value Description
true Send the toast notification directly to the action center; do not raise the toast UI.
false Default. Raise the toast UI as well as adding the notification to the action center.

如果你發送的 notification 是到 Windows Channel,加入這個參數將會收到 error response。
X-WNS-Group Optional Windows Phone only: Used with the X-WNS-Tag header to group notifications in the action center.

〉可用字串不可超過 16 個字元(An alphanumeric string of no more than 16 characters)。
〉可搭配 X-WNS-Tag 一起使用,可做用於同一個 group 但有多個 tag;
X-WNS-Match Situationally required Windows Phone only: Required to identify the target or targets when you remove a toast notification from the action center through the HTTP DELETE method.

使用 HTTP DELETE method 去刪除特定的 toast notification、toast 集合 或是 全部的 toast(藉由 group 或 tag 來識別)。其值可以指定 group 、tag  或全部。如果使用了 HTTP DELETE 其 payload 中給的內容將自動被忽略。寫法如下:
Value Description
type:wns/toast;group=<string value>;tag=<string value> Remove a single notification labelled with both the specified tag and group.
type:wns/toast;group=<string value> Remove all notifications labelled with the specified group.
type:wns/toast;tag=<string value> Remove all notifications labelled with the specified tag.
type:wns/toast;all Clear all of your app's notifications from the action center.

 

C-2. Send notification response

         〉Response parameters

Header name Required/Optional Description
X-WNS-Debug-Trace Optional Debugging information that should be logged to help troubleshoot issues when reporting a problem.
專用於 debug 此次 notification 發送至 WNS 是否有遇到什麼問題。

X-WNS-DeviceConnectionStatus

Optional The device status, returned only if requested in the notification request through the X-WNS-RequestForStatus header.

回傳值類型如下:
Value Description
connected The device is online and connected to WNS.
disconnected The device is offline and not connected to WNS.
tempconnected The device temporarily lost connection to WNS, for instance when a 3G connection is dropped or the wireless switch on a laptop is thrown. It is seen by the Notification Client Platform as a temporary interruption rather than an intentional disconnection.
X-WNS-Error-Description Optional A human-readable error string that should be logged to help with debugging.
X-WNS-Msg-ID Optional A unique identifier for the notification, used for debugging purposes.
When reporting a problem, this information should be logged to help in troubleshooting.
This header is used to provide the caller with an identifier for the notification.
We recommended that this header be logged to help debug issues.
This header, together with the X-WNS-Debug-Trace header, is required when reporting an issue to WNS.
X-WNS-Status Optional Indicates whether WNS successfully received and processed the notification. When reporting a problem, this information should be logged to help in troubleshooting.

其值如下:
Value Description
received

The notification was received and processed by WNS.

Note  This does not guarantee that the device received the notification.

dropped The notification was explicitly dropped because of an error or because the client has explicitly rejected these notifications.
Toast notifications will also be dropped if the device is offline.
channelthrottled The notification was dropped because the app server exceeded the rate limit for this specific channel.

更多相關 response code 的描述與錯誤排除可參考<Response codes>與<Troubleshooting tile, toast, and badge notifications>的說明。

藉由 C 了解向 WNS 發送 notification 的 request 與 response 各有那些參數可以協助處理,往下便說明要怎麼發送 notification 至 WNS。

 

D: 發送 notifications 至特定的 channel URI

     發送 notification 時需要加上 access token,由於 access token 具有期效所以超過時會收到 error,此時要記得去 renew 取得新的 access token。

     採用 HTTP POST 傳送 notification 至 channel URI,其中夾帶上述的 header parameters 與 payload,詳細可參考<Push notification response codes>。    

     需注意 WNS 在收到 cloud service 送出的 notification 是不保證送達(nd-to-end confirmation)的

     相關可用的 payload 被由 XML  schemas 格式來自義如下:

     D-1. . Toast Notification

                 由於 Windows Store App 與 Windows Phone App 可設定的屬性與 template 不完全相同,可參考<The toast template catalog (Windows Runtime apps)>

                 的介紹選擇需要項目。其中 Windows Phone app 可以指定通知的聲音,可參考<The toast audio options catalog (Windows Runtime apps)>的設定。

     D-2.  Tile/Badge Notification:

               Tile 的 template 建議可以搭配 NotificationsExtensions 來使用,詳細的 Tile template 可參考<The tile template catalog (Windows Runtime apps)>。

               Badge 的 template 比較少但 Windows Store App 與 Windows Phone app 不相同,可參考<Badge overview (Windows Runtime apps)>的介紹。

               需注意,由於 Tile 或 Badge 的 notification 在 app 收到與下載後可以存活 3 天,過期的 notification 會被清掉,建議每一個 notification 都設定過期時間,

               確保用戶看到的資料不會相隔太久,可搭配 X-WNS-Expires 在 HTTP header 裡指定。              

     D-3. Raw Notification

              直接傳遞要顯示的內容,不需要符合 XML 的格式,相對的 App 本身收到 notification 需要自定義處理邏輯。

 

由於 tile / badge / toast 都是 XML 格式,所以在 cloud service 發送 HTTP POST 時可以參考上述的 template 文件來加以建立與修改。

〉發送 Toast notification:

     針對 Windows Store App 與 Windows Phone App 的 Toast template 可參考<The toast template catalog (Windows Runtime apps)>。

protected async void OnSendToastClick(object sender, EventArgs e)
{
    XElement toast = new XElement("toast",
                        new XElement("visual",
                            new XElement("binding",
                                new XAttribute("template", "ToastText01"),
                                new XElement("text",
                                    new XAttribute("id", "1"), "from WNSProject Toast"))));
    String channel = txtChannelURI.Text;
    String xml = toast.ToString();
    String result = await SendNotificationToWNS(channel, xml, Application["access_token"].ToString(), "wns/toast");
}

〉發送 Tile 與 Badge notification:

protected async void OnSendTileClick(object sender, EventArgs e)
{
    XElement tile = new XElement("tile",
                        new XElement("visual",
                            new XAttribute("version", "2"),
                            new XElement("binding",
                                new XAttribute("template", "TileSquare150x150PeekImageAndText04"),
                                new XAttribute("fallback", "TileSquarePeekImageAndText04"),
                                new XElement("image",
                                    new XAttribute("id", "1"),
                                    new XAttribute("src", "ms-appx:///Assets/05.png"),
                                    new XAttribute("alt", "")),
                                new XElement("text",
                                    new XAttribute("id", "1"), "from WNSProject tile"))));
    String channel = txtChannelURI.Text;
    String xml = tile.ToString();
    String result = await SendNotificationToWNS(channel, xml, Application["access_token"].ToString(), "wns/tile");
}

     關於 tile 的使用要注意 Windows Store App 與 Windows Phone App 可用的 tile size 不一樣,所以傳送時要特別注意 template 的使用,

     詳細可參考<The tile template catalog (Windows Runtime apps)>。

     相關 Badge 的 notification 如下:

protected async void OnSendBadgeClick(object sender, EventArgs e)
{
   XElement badge = new XElement("badge",
                           new XAttribute("value", "99"));
   String channel = txtChannelURI.Text;
   String xml = badge.ToString();
   String result = await SendNotificationToWNS(channel, xml, Application["access_token"].ToString(), "wns/badge");
}

〉發送 Raw notification:

protected async void OnSendRawClick(object sender, EventArgs e)
{
    String channel = txtChannelURI.Text;
    String xml = "from WNS Prject raw data";
    String result = await SendNotificationToWNS(channel, xml, Application["access_token"].ToString(), "wns/raw");
}

 

以上發送的範例程式均由下方的副程式加以包裝,針對每一個 response 的 status code 做判斷,包括:Unauthorized、NotFound、Gone…等,如下:

private String SendNotificationToWNS(String channel, String payload, String accessToken, String type, Boolean forPhone = false)
{
    //http://notificationserver.microsoft.com/AppKey14035poumason.kkstoreappbeta_2mew08e2h8n4p/TransactionId1/1:14856069679381456243
    HttpWebRequest request = HttpWebRequest.Create(channel) as HttpWebRequest;
    request.Method = "POST";
    request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken));
    request.Headers.Add("X-WNS-Type", type);

 
    //request.Headers.Add("X-WNS-Cache-Policy", "cache");
    //request.Headers.Add("X-WNS-RequestForStatus", "true");
    //request.Headers.Add("X-WNS-Tag", "WNSProjectTest");
    //request.Headers.Add("X-WNS-Group", "WNSGroup");
    //request.Headers.Add("X-WNS-TTL", "300");
    //if (forPhone)
    //{
    //    request.Headers.Add("X-WNS-SuppressPopup", "true");
    //}

 
    if (type == "wns/raw")
    {
        request.ContentType = "application/octet-stream";
    }
    else
    {
        request.ContentType = "text/xml";
    }

 
    byte[] contentInBytes = Encoding.UTF8.GetBytes(payload);
    request.ContentLength = contentInBytes.Length;

 
    using (Stream requestStream = request.GetRequestStream())
    {
        requestStream.Write(contentInBytes, 0, contentInBytes.Length);
    }
    try
    {
        using (HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse())
        {
            return webResponse.Headers["X-WNS-Msg-ID"];
        }
    }
    catch (WebException ex)
    {
        HttpStatusCode status = ((HttpWebResponse)ex.Response).StatusCode;

 
        if (status == HttpStatusCode.Unauthorized)
        {
            String token = GetAccessToken();
            return SendNotificationToWNS(channel, payload, token, type, forPhone);
        }
        else if (status == HttpStatusCode.NotFound || status == HttpStatusCode.Gone)
        {
            return "The channel URI is no longer valid";
        }
        else
        {
            string[] debugOutput = {
                               status.ToString(),
                               ex.Response.Headers["X-WNS-Debug-Trace"],
                               ex.Response.Headers["X-WNS-Error-Description"],
                               ex.Response.Headers["X-WNS-Msg-ID"],
                               ex.Response.Headers["X-WNS-Status"]
                           };
            return string.Join(" | ", debugOutput);       
        }
    }
}

 

更多相關的發送 push notification 內容可參考

<Windows Push Notification Services (WNS) overview (Windows Runtime apps)>與<Quickstart: Sending a push notification (XAML)>。

 

執行結果:

image

將發送訊息後取得的 WNS-Msg-ID 顯示出來。

 

[範例程式]

詳細實作的範例可以參考:<Quickstart: Sending a push notification (XAML)> 介紹的內容。

 

[補充]

1. 實作 WNS 整合時不管是 App 或 Server 端可參考:

     <Guidelines and checklist for push notifications>與<Guidelines and checklist for tiles and badges>的內容來開發。

2. 發送 notification 流程:

  Cloud service to WNS communication

Important notes

1. WNS does not guarantee the reliability or latency of a notification.

2. Notifications should never include confidential or sensitive data.

3. To send a notification, the cloud service must first authenticate with WNS and receive an access token.

    An access token allows a cloud service to send notifications to only the single app that the token was created for.

    One access token cannot be used to send notifications across multiple apps.

    If your cloud service supports multiple apps, it must provide the correct access token for the app when pushing a notification to each channel URI.

4. When the device is offline, by default WNS will store up to five tile notifications (if queuing is enabled; otherwise, one tile notification)

     and one badge notification for each channel URI, and no raw notifications.

     This default caching behavior can be changed through the X-WNS-Cache-Policy header.

     Note that toast notifications are never stored when the device is offline.

5. In scenarios where the notification content is personalized to the user, WNS recommends that the cloud service send those updates immediately when they are received.

     Examples of this scenario include social media feed updates, instant communication invitations, new message notification, or alerts. Alternatively,

     you can have scenarios where the same generic update is frequently delivered to a large subset of your users;

     for example, weather, stock, and news updates. WNS guidelines specify that the frequency of these updates should be at most one every 30 minutes.

     The end user or WNS may determine more frequent routine updates to be abusive.

======

本篇先介紹如果建立一個 Server 來保存 App 向註冊的 Channel ID 與發送特定的 Notification 至 Windows Notification Service 。

下一篇將針對 Universal App 如果實作支援 WNS 的 App,盡可能把相同的程式可以共用,再細部針對不同的 Notification 區別來支援各平台的使用。

希望對大家有所幫助,謝謝。

 

References

Windows Push Notification Services (WNS) overview (Windows Runtime apps)

Sending push notifications with WNS (XAML)

Code generated by the push notification wizard (專案直接整合 Azure Mobile Service)

     〉Azure Mobile Services REST API Reference

     〉Register table operations in Azure Mobile Services

     〉Mobile Services server script reference

How to authenticate with the Windows Push Notification Service (WNS) (Windows Runtime apps)

Delivering scheduled, periodic, and push notifications (XAML) (重要)

Working with tiles, badges, and toast notifications (重要)

Guidelines and checklist for push notifications (重要)

Guidelines and checklist for tiles and badges (重要)

Troubleshooting tile, toast, and badge notifications

Scheduling notifications

Periodic notifications

Push notifications with WNS

Using live tiles with different app types (Windows Runtime apps)

Using the notification queue (Windows Runtime apps)

 

Dotblogs Tags: ,