共用方式為


Windows 推播通知服務 (WNS) 概觀

Windows 推播通知服務(WNS)可讓第三方開發人員從自己的雲端服務傳送快顯通知(Toast)、動態磚、徽章和原始資料更新。 這提供一種機制,以有效率且可靠的方式,為使用者提供新的更新。

運作方式

下圖顯示傳送推播通知的完整數據流。 它牽涉到下列步驟:

  1. 您的應用程式會向 WNS 要求推播通知通道。
  2. Windows 會要求 WNS 建立通知通道。 此通道會以統一資源標識碼 (URI) 的形式傳回呼叫裝置。
  3. WNS 會將通知通道 URI 傳回至您的應用程式。
  4. 您的應用程式會將 URI 傳送至您自己的雲端服務。 接著,您會將 URI 儲存在自己的雲端服務上,以便在傳送通知時存取 URI。 URI 是您自己的應用程式與您自己的服務之間的介面;您必須負責使用安全且安全的 Web 標準來實作此介面。
  5. 當雲端服務有要傳送的更新時,它會使用通道 URI 通知 WNS。 這是透過安全套接字層(SSL)發送 HTTP POST 請求來實現的,包括通知負載。 此步驟需要驗證。
  6. WNS 會收到要求,並將通知路由傳送至適當的裝置。

wns 推播通知的數據流圖

註冊您的應用程式並接收雲端服務的認證

在您能夠使用 WNS 傳送通知之前,您的應用程式必須已在市集儀表板上註冊,如這裡所述

申請通知渠道

當能夠接收推播通知的應用程式執行時,必須先透過 CreatePushNotificationChannelForApplicationAsync 方法要求通知頻道。 如需完整的討論和範例程式代碼,請參閱 如何要求、建立和儲存通知通道。 此 API 會傳回一個通道 URI,此 URI 唯一連結至呼叫的應用程式及其動態磚,並用於傳送所有類型的通知。

應用程式成功建立通道 URI 之後,會將它傳送至其雲端服務,以及任何應該與此 URI 相關聯的應用程式特定元數據。

重要注意

  • 我們不保證應用程式的通知通道 URI 一律會維持不變。 我們建議應用程式在每次執行時要求新的通道,並在 URI 變更時更新其服務。 開發人員不應該修改通道 URI,而且應該將其視為黑箱字串。 此時,通道 URI 會在 30 天后到期。 如果您的 Windows 10 應用程式會在背景定期更新其通道,您可以下載適用於 Windows 8.1 的 推播和定期通知範例,並重複使用其原始程式碼和/或所示範的模式。
  • 雲端服務和用戶端應用程式之間的介面是由開發人員實作。 我們建議應用程式使用自己的服務進行驗證程式,並透過 HTTPS 等安全通訊協定傳輸數據。
  • 雲端服務務必確保通道 URI 使用網域「notify.windows.com」。 服務不應將通知推送至任何其他網域上的通道。 如果應用程式的回呼遭到入侵,惡意攻擊者可能會提交通道 URI 來詐騙 WNS。 若未檢查網域,您的雲端服務可能會不知情地向此攻擊者揭露資訊。 通道 URI 的子域可能會變更,而且在驗證通道 URI 時不應考慮。
  • 如果您的雲端服務嘗試將通知傳遞至過期的通道,WNS 會傳回 回應碼 410。 根據該代碼,您的服務應停止將通知傳送至該 URI。

驗證雲端服務

若要傳送通知,雲端服務必須透過 WNS 進行驗證。 當您在 Microsoft 商城儀表板註冊應用程式時,這個流程的第一步就會發生。 在註冊程式期間,您的應用程式會獲得套件安全性識別碼 (SID) 和秘密密鑰。 雲端服務會使用此資訊向WNS進行驗證。

WNS 驗證配置是使用 OAuth 2.0 通訊協定中的客戶端認證配置檔來實作。 雲端服務透過提供其憑證(封裝 SID 和密鑰)來向 WNS 證明身份。 作為回應,它會接收存取令牌。 此存取令牌可讓雲端服務傳送通知。 每個傳送至 WNS 的通知要求都需要令牌。

概括而言,信息鏈結如下:

  1. 雲端服務會遵循 OAuth 2.0 通訊協定,透過 HTTPS 將其認證傳送至 WNS。 這會向 WNS 驗證服務。
  2. 如果驗證成功,WNS 會傳回存取令牌。 此存取令牌會用於所有後續通知要求,直到到期為止。

雲端服務驗證的 wns 圖表

在 WNS 的驗證中,雲端服務會透過安全套接字層 (SSL) 提交 HTTP 要求。 參數會以“application/x-www-for-urlencoded” 格式提供。 在 [client_id] 欄位中提供您的套件 SID,並在 [client_secret] 欄位中提供秘密密鑰,如下列範例所示。 有關語法的詳細信息,請參閱 存取權杖請求 參考資料。

Note

這只是一個範例,而不是您可以成功在自己的程式代碼中使用的複製貼上的程式碼。 

 POST /accesstoken.srf HTTP/1.1
 Content-Type: application/x-www-form-urlencoded
 Host: https://login.live.com
 Content-Length: 211
 
 grant_type=client_credentials&client_id=ms-app%3a%2f%2fS-1-15-2-2972962901-2322836549-3722629029-1345238579-3987825745-2155616079-650196962&client_secret=Vex8L9WOFZuj95euaLrvSH7XyoDhLJc7&scope=notify.windows.com

WNS 會驗證雲端服務,如果成功,則會傳送“200 OK” 的回應。 存取令牌會使用 「application/json」 媒體類型,在 HTTP 回應主體中包含的參數中傳回。 服務收到存取令牌之後,您就可以傳送通知。

下列範例顯示成功的驗證回應,包括存取令牌。 如需語法詳細數據,請參閱 推播通知服務要求和響應標頭

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

重要注意

  • 此程序支援的 OAuth 2.0 通訊協定遵循 V16 版草稿。
  • OAuth 徵求意見稿(RFC)使用「客戶端」一詞來指稱雲端服務。
  • 當 OAuth 草稿完成時,此程式可能會變更。
  • 存取權杖可以用於多個通知請求的重複使用。 這可讓雲端服務只驗證一次,以傳送許多通知。 不過,當存取令牌到期時,雲端服務必須再次進行驗證,才能接收新的存取令牌。

傳送通知

使用通道 URI 時,雲端服務可以在使用者有更新時傳送通知。

上述存取令牌可以重複使用給多個通知要求;雲端伺服器不需要為每個通知要求新的存取令牌。 如果存取令牌已過期,通知要求會傳回錯誤。 建議您不要嘗試在拒絕存取令牌時多次重新傳送通知。 如果您遇到此錯誤,您必須要求新的存取令牌並重新傳送通知。 如需確切的錯誤碼,請參閱 推播通知回應碼

  1. 雲端服務會向通道 URI 發送 HTTP POST 請求。 此要求必須透過 SSL 提出,並包含必要的標頭和通知承載。 授權標頭必須包含取得的存取令牌以進行授權。

    這裡會顯示一個範例要求。 如需語法詳細資訊,請參閱 推播通知回應碼

    如需撰寫通知承載的詳細資訊,請參閱 快速入門:傳送推播通知。 磚、快顯通知或徽章推播通知的有效負載會以 XML 內容提供,這些內容需符合各自定義的 調適型磚架構傳統磚架構。 原始通知的承載沒有指定的結構。 它是由應用程式嚴格定義的。

     POST https://cloud.notify.windows.com/?token=AQE%bU%2fSjZOCvRjjpILow%3d%3d HTTP/1.1
     Content-Type: text/xml
     X-WNS-Type: wns/tile
     Authorization: Bearer EgAcAQMAAAAALYAAY/c+Huwi3Fv4Ck10UrKNmtxRO6Njk2MgA=
     Host: cloud.notify.windows.com
     Content-Length: 24
    
     <body>
     ....
    
  2. WNS 會回應指出已收到通知,且將在下一個可用的機會傳遞。 不過,WNS 不會提供裝置或應用程式已收到通知的端對端確認。

下圖說明資料流:

用於傳送通知的 wns 圖表

重要注意

  • WNS 不保證通知的可靠性或延遲。
  • 通知不得包含機密、敏感性或個人資料。
  • 若要傳送通知,雲端服務必須先向WNS進行驗證,並接收存取令牌。
  • 存取令牌只允許雲端服務將通知傳送至建立令牌的單一應用程式。 一個存取令牌無法用來跨多個應用程式傳送通知。 因此,如果您的雲端服務支援多個應用程式,則必須在將通知推送至每個通道 URI 時,為應用程式提供正確的存取令牌。
  • 當裝置離線時,根據預設設定,WNS 會針對每個通道 URI 儲存每一種通知類型的其中一個(磚、徽章、通知卡),並且不會儲存任何原始通知。
  • 在通知內容個人化給使用者的案例中,WNS 建議雲端服務在收到這些更新時立即傳送這些更新。 此案例的範例包括社交媒體摘要更新、立即通訊邀請、新訊息通知或警示。 或者,您可以有這樣的情境:相同的一般更新經常發送給大量用戶群體,例如天氣、股票和新聞更新。 WNS 指導方針指定這些更新的頻率應該最多每 30 分鐘一次。 終端使用者或 WNS 可能會判定過於頻繁的例行更新為濫用行為。
  • Windows 通知平臺會維護與 WNS 的定期數據連線,讓套接字保持運作良好。 如果沒有應用程式要求或使用通知通道,則不會建立套接字。

磁貼和徽章通知的過期或失效

根據預設,磚和徽章通知會在下載后的三天到期。 當通知到期時,內容會從磚或佇列中移除,且不再向用戶顯示。 最佳做法是在所有磁貼和徽章通知上設置過期時間(使用適合您應用程式的時間),以確保磁貼的內容不會持續保存超過其相關性。 明確到期時間對於已定義生命周期的內容而言很重要。 如果您的雲端服務停止傳送通知,或用戶長時間中斷網路連線,這也可確保移除過時的內容。

您的雲端服務可以透過設定 X-WNS-TTL HTTP 標頭,為每個通知設定到期時間,並指定通知在發送後仍然有效的時間長度(以秒為單位)。 如需詳細資訊,請參閱 推播通知服務要求和響應標頭

例如,在股市活躍交易日期間,您可以將股票價格更新的有效時間設定為兩倍於您的發送間隔(例如,如果您每半小時發送一次通知,則有效時間為收到後一小時)。 另一個範例是,新聞應用程式可能會判斷有一天是每日新聞磚更新的適當到期時間。

推播通知和省電模式

省電模式可藉由限制裝置上的背景活動來延長電池使用時間。 Windows 10 可讓使用者設定省電模式,在電池低於指定閾值時自動開啟。 當省電模式開啟時,會停用推播通知的接收以節省能源。 但有一些例外。 下列 Windows 10 省電模式設定 (可在 Windows 設定中找到) 可讓您的應用程式接收推播通知,即使省電模式已開啟。

  • 在省電模式中允許來自任何應用程式的推播通知:此設定可讓所有應用程式在省電模式開啟時接收推播通知。 請注意,此設定僅適用於 Windows 10 傳統型版本(家用版、專業版、企業版和教育版)。
  • 一律允許:此設定可讓特定應用程式在省電模式開啟時在背景執行,包括接收推播通知。 此清單是由用戶手動維護。

無法檢查這兩個設定的狀態,但您可以檢查省電模式的狀態。 在 Windows 10 中,使用 EnergySaverStatus 屬性來檢查省電模式狀態。 您的應用程式也可以使用 EnergySaverStatusChanged 事件來監聽省電模式的變更。

如果您的應用程式嚴重依賴推播通知,建議您通知使用者,他們可能不會在省電模式開啟時收到通知,並方便他們調整 省電模式設定。 使用 Windows 中的省電模式 URI 配置,ms-settings:batterysaver-settings 您可以提供一個指向 Windows 設定的便利連結。

Tip

通知使用者有關省電模式設定時,建議您提供未來隱藏訊息的方法。 例如,下列範例中的 [dontAskMeAgainBox] 複選框會在 本地設定中持續保存使用者的喜好設定。

以下範例說明如何檢查是否在 Windows 10 中開啟省電模式。 此範例會通知使用者,並啟動 [設定] 以取得 省電模式設定dontAskAgainSetting可讓使用者在不想再次收到通知時隱藏訊息。

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Windows.System;
using Windows.System.Power;
...
...
async public void CheckForEnergySaving()
{
   //Get reminder preference from LocalSettings
   bool dontAskAgain;
   var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
   object dontAskSetting = localSettings.Values["dontAskAgainSetting"];
   if (dontAskSetting == null)
   {  // Setting does not exist
      dontAskAgain = false;
   }
   else
   {  // Retrieve setting value
      dontAskAgain = Convert.ToBoolean(dontAskSetting);
   }
   
   // Check if battery saver is on and that it's okay to raise dialog
   if ((PowerManager.EnergySaverStatus == EnergySaverStatus.On)
         && (dontAskAgain == false))
   {
      // Check dialog results
      ContentDialogResult dialogResult = await saveEnergyDialog.ShowAsync();
      if (dialogResult == ContentDialogResult.Primary)
      {
         // Launch battery saver settings (settings are available only when a battery is present)
         await Launcher.LaunchUriAsync(new Uri("ms-settings:batterysaver-settings"));
      }

      // Save reminder preference
      if (dontAskAgainBox.IsChecked == true)
      {  // Don't raise dialog again
         localSettings.Values["dontAskAgainSetting"] = "true";
      }
   }
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.System.Power.h>
#include <winrt/Windows.UI.Xaml.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Navigation.h>
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::System;
using namespace winrt::Windows::System::Power;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;
...
winrt::fire_and_forget CheckForEnergySaving()
{
    // Get reminder preference from LocalSettings.
    bool dontAskAgain{ false };
    auto localSettings = ApplicationData::Current().LocalSettings();
    IInspectable dontAskSetting = localSettings.Values().Lookup(L"dontAskAgainSetting");
    if (!dontAskSetting)
    {
        // Setting doesn't exist.
        dontAskAgain = false;
    }
    else
    {
        // Retrieve setting value
        dontAskAgain = winrt::unbox_value<bool>(dontAskSetting);
    }

    // Check whether battery saver is on, and whether it's okay to raise dialog.
    if ((PowerManager::EnergySaverStatus() == EnergySaverStatus::On) && (!dontAskAgain))
    {
        // Check dialog results.
        ContentDialogResult dialogResult = co_await saveEnergyDialog().ShowAsync();
        if (dialogResult == ContentDialogResult::Primary)
        {
            // Launch battery saver settings
            // (settings are available only when a battery is present).
            co_await Launcher::LaunchUriAsync(Uri(L"ms-settings:batterysaver-settings"));
        }

        // Save reminder preference.
        if (dontAskAgainBox().IsChecked())
        {
            // Don't raise the dialog again.
            localSettings.Values().Insert(L"dontAskAgainSetting", winrt::box_value(true));
        }
    }
}

這是用於此範例中所精選的 ContentDialog 的 XAML。

<ContentDialog x:Name="saveEnergyDialog"
               PrimaryButtonText="Open battery saver settings"
               SecondaryButtonText="Ignore"
               Title="Battery saver is on."> 
   <StackPanel>
      <TextBlock TextWrapping="WrapWholeWords">
         <LineBreak/><Run>Battery saver is on and you may 
          not receive push notifications.</Run><LineBreak/>
         <LineBreak/><Run>You can choose to allow this app to work normally
         while in battery saver, including receiving push notifications.</Run>
         <LineBreak/>
      </TextBlock>
      <CheckBox x:Name="dontAskAgainBox" Content="OK, got it."/>
   </StackPanel>
</ContentDialog>