快速入門:傳送推播通知 (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 標頭會指定這是磚、快顯通知、徽章或原始通知。
  • 根據 X-WNS-Type 的值 來設定 Content-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 正在節流此通道。 實作重試策略,以指數方式減少傳送的通知數量,以防止再次進行節流。 此外,重新思考導致您未處理的案例。 您將藉由限制傳送給新增真實值的通知,來提供更豐富的用戶體驗。

    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 的通知。 藉由變更類型參數的值並調整其他標頭,此程式代碼可用於快顯通知、磚、徽章或原始推播通知。 您可以使用此函式作為雲端伺服器程式代碼的一部分。

請注意,此函式中的錯誤處理包含存取令牌已過期的情況。 在此情況下,它會呼叫另一個雲端伺服器函式,以使用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 內容來定義您的通知,並將該通知從您的伺服器傳送至您的應用程式。