クイック スタート: プッシュ通知の送信 (XAML)
クラウド サーバーは、Windows Push Notification Services (WNS) 経由でアプリにプッシュ通知を送信できます。 この手順は、タイル、トースト、バッジ、直接プッシュ通知に適用されます。
目的: タイル、トースト、バッジ、または直接プッシュ通知を作成して送信する。
前提条件
このトピックを理解するか、またはここで提供されているコードを使用するには、次のものが必要になります。
- HTTP 通信に関する知識。
- 認証されたクラウド サーバー。 詳細については、「Windows プッシュ通知サービス (WNS) に対して認証する方法」を参照してください。
- クラウド サーバーがアプリと通信するために使用できる登録済みのチャネル。 詳細については、「通知チャネルを要求、作成、保存する方法」を参照してください。
- アプリのマニフェストで定義されている、通知を受信するためのアプリの既存のタイル (直接通知を送信している場合を除く)。 詳細については、Microsoft Visual Studio マニフェスト エディターを使用した既定のタイルの作成に関するクイック スタートを参照してください。
- XML と、ドキュメント オブジェクト モデル (DOM) API を使用したその操作に関する知識。
- 直接通知の場合は、直接通知を受信するようにアプリを構成する必要があります。 詳細については、「クイック スタート: 実行中のアプリのプッシュ通知の中断」および「クイック スタート: 直接通知のバックグラウンド タスクの作成と登録」を参照してください。
手順
1. 必要な名前空間参照を含める
このトピックで示されている例はそのまま使用できますが、コードに次の名前空間参照を含める必要があります。
using System.Net;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Web;
using System.Text;
2. HTTP POST 要求を作成する
uri
パラメーターは、アプリから要求され、クラウド サーバーに渡されるチャネル URI (Uniform Resource Identifier) です。 詳細については、「通知チャネルを要求、作成、保存する方法」を参照してください。
HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
request.Method = "POST";
3. 必須ヘッダーを追加する
すべてのプッシュ通知に含める必要がある必須ヘッダーには、X-WNS-Type、Content-Type、Content-Length、Authorization の 4 つがあります。
- X-WNS-Type ヘッダーは、これがタイル、トースト、バッジ、直接通知のいずれであるかを指定します。
- Content-Type は、X-WNS-Type の値に応じて設定されます。
- Content-Length は、含まれている通知ペイロードのサイズを指定します。
- Authorization ヘッダーは、このチャネル経由で、このユーザーにプッシュ通知を送信できる認証資格情報を指定します。
Authorization ヘッダーの accessToken パラメーターは、クラウド サーバーが認証を要求したときに WNS から受信された、サーバーに格納されているアクセス トークンを指定します。 このアクセス トークンがないと、通知は拒否されます。
使用できるヘッダーの完全な一覧については、「プッシュ通知サービスの要求ヘッダーと応答ヘッダー」を参照してください。
request.Headers.Add("X-WNS-Type", notificationType);
request.ContentType = contentType;
request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken.AccessToken));
4. 準備されたコンテンツを追加する
HTTP 要求に関する限り、通知の XML コンテンツは要求本文内のデータ BLOB です。 たとえば、その XML が X-WNS-Type の仕様に一致することの確認は行われません。 コンテンツは XML ペイロードとして指定され、ここで、バイトのストリームとして要求に追加されます。
byte[] contentInBytes = Encoding.UTF8.GetBytes(xml);
using (Stream requestStream = request.GetRequestStream())
requestStream.Write(contentInBytes, 0, contentInBytes.Length);
5. 通知の受信を確認する WNS からの応答をリッスンする
注意
通知の配信確認を受信することはなく、WNS によって受信されたことを示す受信確認を受信するだけです。
using (HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse())
return webResponse.StatusCode.ToString();
6. WNS 応答コードを処理する
App Service が通知を送信したときに受信できる応答コードには多くのものがあります。 これらの応答コードのいくつかは他のものより一般的であり、catch ブロック内で容易に処理できます。
catch (WebException webException)
{
HttpStatusCode status = ((HttpWebResponse)webException.Response).StatusCode;
HttpStatusCode.Unauthorized: 指定したアクセス トークンの有効期限が切れました。 新しいものを取得してから、通知の送信を再試行してください。 キャッシュされたアクセス トークンは 24 時間後に有効期限が切れるため、この応答は、少なくとも 1 日に 1 回 WNS から受信することを予測できます。 最大の再試行ポリシーを実装することをお勧めします。
if (status == HttpStatusCode.Unauthorized)
{
GetAccessToken(secret, sid);
return PostToWns(uri, xml, secret, sid, notificationType, contentType);
}
HttpStatusCode.Gone/HttpStatusCode.NotFound: チャネル URI が有効ではなくなりました。 このチャネルをデータベースから削除して、それ以上の通知がそこに送信されないようにしてください。 このユーザーが次回アプリを起動したら、新しい WNS チャネルを要求します。 アプリは、そのチャネルが変更されたことを検出する必要があります。それにより、アプリ サーバーに新しいチャネル URI を送信するようアプリがトリガーされます。 詳細については、「通知チャネルを要求、作成、保存する方法」を参照してください。
else if (status == HttpStatusCode.Gone || status == HttpStatusCode.NotFound)
{
return "";
}
HttpStatusCode.NotAcceptable: このチャネルは WNS によって調整されています。 再び調整されないようにするために、送信される通知の量を指数関数的に減らす再試行戦略を実装してください。 また、通知が調整される原因となっているシナリオを再考します。 true 値を追加するチャネルに送信する通知を制限することによって、より豊かなユーザー エクスペリエンスが提供されます。
else if (status == HttpStatusCode.NotAcceptable)
{
return "";
}
その他の応答コード: WNS が、あまり一般的ではない応答コードで応答しました。 デバッグに役立てるために、このコードをログに記録します。 WNS 応答コードの完全な一覧については、「プッシュ通知サービスの要求ヘッダーと応答ヘッダー」を参照してください。
else
{
string[] debugOutput = {
status.ToString(),
webException.Response.Headers["X-WNS-Debug-Trace"],
webException.Response.Headers["X-WNS-Error-Description"],
webException.Response.Headers["X-WNS-Msg-ID"],
webException.Response.Headers["X-WNS-Status"]
};
return string.Join(" | ", debugOutput);
}
7. コードを 1 つの関数にカプセル化する
次の例では、前の手順で示されているコードを 1 つの関数にパッケージ化します。 この関数では、WNS に送信される通知が含まれている HTTP POST 要求を作成します。 type パラメーターの値を変更し、追加ヘッダーを調整すれば、このコードをトースト、タイル、バッジ、または直接プッシュ通知に使用できます。 この関数は、クラウド サーバー コードの一部として使用できます。
この関数内のエラー処理に、アクセス トークンの有効期限が切れた状況が含まれていることに注意してください。 この場合は、新しいアクセス トークンを取得するために WNS に対して再認証する別のクラウド サーバー関数を呼び出しています。 それが次に、元の関数の新しい呼び出しを行います。
// Post to WNS
public string PostToWns(string secret, string sid, string uri, string xml, string notificationType, string contentType)
{
try
{
// You should cache this access token.
var accessToken = GetAccessToken(secret, sid);
byte[] contentInBytes = Encoding.UTF8.GetBytes(xml);
HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
request.Method = "POST";
request.Headers.Add("X-WNS-Type", notificationType);
request.ContentType = contentType;
request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken.AccessToken));
using (Stream requestStream = request.GetRequestStream())
requestStream.Write(contentInBytes, 0, contentInBytes.Length);
using (HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse())
return webResponse.StatusCode.ToString();
}
catch (WebException webException)
{
HttpStatusCode status = ((HttpWebResponse)webException.Response).StatusCode;
if (status == HttpStatusCode.Unauthorized)
{
// The access token you presented has expired. Get a new one and then try sending
// your notification again.
// Because your cached access token expires after 24 hours, you can expect to get
// this response from WNS at least once a day.
GetAccessToken(secret, sid);
// We recommend that you implement a maximum retry policy.
return PostToWns(uri, xml, secret, sid, notificationType, contentType);
}
else if (status == HttpStatusCode.Gone || status == HttpStatusCode.NotFound)
{
// The channel URI is no longer valid.
// Remove this channel from your database to prevent further attempts
// to send notifications to it.
// The next time that this user launches your app, request a new WNS channel.
// Your app should detect that its channel has changed, which should trigger
// the app to send the new channel URI to your app server.
return "";
}
else if (status == HttpStatusCode.NotAcceptable)
{
// This channel is being throttled by WNS.
// Implement a retry strategy that exponentially reduces the amount of
// notifications being sent in order to prevent being throttled again.
// Also, consider the scenarios that are causing your notifications to be throttled.
// You will provide a richer user experience by limiting the notifications you send
// to those that add true value.
return "";
}
else
{
// WNS responded with a less common error. Log this error to assist in debugging.
// You can see a full list of WNS response codes here:
// https://msdn.microsoft.com/library/windows/apps/hh868245.aspx#wnsresponsecodes
string[] debugOutput = {
status.ToString(),
webException.Response.Headers["X-WNS-Debug-Trace"],
webException.Response.Headers["X-WNS-Error-Description"],
webException.Response.Headers["X-WNS-Msg-ID"],
webException.Response.Headers["X-WNS-Status"]
};
return string.Join(" | ", debugOutput);
}
}
catch (Exception ex)
{
return "EXCEPTION: " + ex.Message;
}
}
// Authorization
[DataContract]
public class OAuthToken
{
[DataMember(Name = "access_token")]
public string AccessToken { get; set; }
[DataMember(Name = "token_type")]
public string TokenType { get; set; }
}
private OAuthToken GetOAuthTokenFromJson(string jsonString)
{
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
{
var ser = new DataContractJsonSerializer(typeof(OAuthToken));
var oAuthToken = (OAuthToken)ser.ReadObject(ms);
return oAuthToken;
}
}
protected OAuthToken GetAccessToken(string secret, string sid)
{
var urlEncodedSecret = HttpUtility.UrlEncode(secret);
var urlEncodedSid = HttpUtility.UrlEncode(sid);
var body = String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com",
urlEncodedSid,
urlEncodedSecret);
string response;
using (var client = new WebClient())
{
client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
response = client.UploadString("https://login.live.com/accesstoken.srf", body);
}
return GetOAuthTokenFromJson(response);
}
トースト プッシュ通知のための HTTP POST 要求のコンテンツの例を次に示します。
POST https://db3.notify.windows.com/?token=AgUAAADCQmTg7OMlCg%2fK0K8rBPcBqHuy%2b1rTSNPMuIzF6BtvpRdT7DM4j%2fs%2bNNm8z5l1QKZMtyjByKW5uXqb9V7hIAeA3i8FoKR%2f49ZnGgyUkAhzix%2fuSuasL3jalk7562F4Bpw%3d HTTP/1.1
Authorization: Bearer EgAaAQMAAAAEgAAACoAAPzCGedIbQb9vRfPF2Lxy3K//QZB79mLTgK
X-WNS-RequestForStatus: true
X-WNS-Type: wns/toast
Content-Type: text/xml
Host: db3.notify.windows.com
Content-Length: 196
<toast launch="">
<visual lang="en-US">
<binding template="ToastImageAndText01">
<image id="1" src="World" />
<text id="1">Hello</text>
</binding>
</visual>
</toast>
HTTP POST 要求に応答して WNS によってクラウド サーバーに送信される HTTP 応答の例を次に示します。
HTTP/1.1 200 OK
Content-Length: 0
X-WNS-DEVICECONNECTIONSTATUS: connected
X-WNS-STATUS: received
X-WNS-MSG-ID: 3CE38FF109E03A74
X-WNS-DEBUG-TRACE: DB3WNS4011534
まとめ
このクイック スタートでは、WNS に送信する HTTP POST 要求を作成しました。 WNS が次に、その通知をアプリに配信します。 この時点までに、アプリを登録し、クラウド サーバーを WNS に対して認証し、通知を定義するための XML コンテンツを作成し、その通知をサーバーからアプリに送信しました。
関連トピック
Windows developer