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 的運作原理如下圖:
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:
「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:
|
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。寫法:
|
||||||||||
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 是否符合。可用值如下:
|
||||||||||
X-WNS-Cache-Policy | Optional | Enables or disables notification caching. This header applies only to tile, badge, and 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。
|
||||||||||
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。
如果你發送的 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 中給的內容將自動被忽略。寫法如下:
|
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 是否有遇到什麼問題。 |
||||||||
Optional | The device status, returned only if requested in the notification request through the X-WNS-RequestForStatus header. 回傳值類型如下:
|
|||||||||
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. 其值如下:
|
更多相關 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)>的設定。
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)>。
執行結果:
將發送訊息後取得的 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 流程:
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
〉Using live tiles with different app types (Windows Runtime apps)
〉Using the notification queue (Windows Runtime apps)