共用方式為


快速入門:傳送推播通知 (XAML)

您的雲端伺服器可以透過 Windows 推播通知服務 (WNS) 將推播通知傳送至您的應用程式。 此程式適用於磚、快顯、徽章和原始推播通知。

目標:建立並傳送磚、快顯、徽章或原始推播通知。

必要條件

為了解本主題或使用主題中提供的程式碼,您需要:

指示

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),並且會傳遞至雲端伺服器。 如需詳細資訊,請參閱如何要求、建立和儲存通知通道

HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
request.Method = "POST";

3. 新增必要的標頭

所有推播通知都必須包含四個必要的標頭:X-WNS-Type、Content-Type、Content-Length, 和 Authorization

  • 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 回應碼

您的應用程式服務傳送通知時,可以接收許多回應碼。 當中有些回應碼較為常見,且可以在 catch 區塊中輕鬆處理。

catch (WebException webException)
{
    HttpStatusCode status = ((HttpWebResponse)webException.Response).StatusCode;

HttpStatusCode.Unauthorized:您提供的存取權杖已過期。 取得新的存取權杖,然後再次嘗試傳送通知。 由於快取的存取權杖會在 24 小時後過期,因此您可以預期每天至少會收到一次來自 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. 將程式碼封裝成單一函式

下列範例會將上述步驟中所提供的程式碼封裝成單一函式。 此函式會撰寫 HTTP POST 要求,其中包含要傳送至 WNS 的通知。 藉由變更 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 回應會由 WNS 傳送至雲端伺服器,以回應 HTTP POST 要求。

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 內容來定義您的通知,並將該通知從您的伺服器傳送至您的應用程式。