背水一戰 Windows 10 (121) - 後臺任務: 推送通知

[源碼下載]


html

背水一戰 Windows 10 (121) - 後臺任務: 推送通知



做者:webabcd


介紹
背水一戰 Windows 10 之 後臺任務html5

  • 推送通知



示例
演示如何接收推送通知
/WebApi/PushNotificationController.csc++

/*
 * 演示如何向 app 推送通知
 * 因爲本例沒有上商店,因此本例是沒法演示的,須要看演示效果的話運行一下本身寫的「打字通」的 /TypingGame/PushNotification/Sample.xaml,而後用其生成的 channel 地址在 /WebApi/Controllers/PushNotificationController.cs 推送通知
 *
 *
 * 注:
 * 關於推送通知服務請求和響應頭的詳細說明參見:https://msdn.microsoft.com/zh-cn/library/windows/apps/hh465435.aspx
 */

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Threading.Tasks;

namespace WebApi.Controllers
{
    public class PushNotificationController : ApiController
    {
        [HttpGet]
        public async Task<HttpResponseMessage> Get()
        {
            // 向某個 app 推送通知的 channel 地址(經過客戶端的 PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync() 方法生成)
            string notifyUrl = "https://hk2.notify.windows.com/?token=AwYAAAANZzLsCX%2fl1aavCSQhi%2fdEBO5wdplj7S4a3o4t8wGSGo05hRE6VC7xEMCFtGDrVuV%2f9J2ItuVri1F4Z0YNjtbuCqf6LQvov0UE3%2flD1sP1poaS1Qp30UQ%2fWVKVUBCjPFuWFLuyuq7UuuTvJcCcQzey";

            // 在商店後臺的 dashboard 中的「Package SID」中能夠找到此值(能夠在 https://apps.dev.microsoft.com/ 中查找)
            string sid = "ms-app://s-1-15-2-1792688850-3283391166-**********-**********-**********-1809961044-230289451";
            // 在商店後臺的 dashboard 中的「Application Secrets」中能夠找到此值(能夠在 https://apps.dev.microsoft.com/ 中查找)
            string secret = "koghs4zz*************S+5sEoqoNb4";

            OAuthHelper oAuth = new OAuthHelper();
            OAuthToken token = oAuth.GetAccessToken(secret, sid);

            HttpResponseMessage result = null;

            try
            {
                HttpClient httpClient = new HttpClient();
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, notifyUrl);

                // 推送消息的類型:wns/toast | wns/badge | wns/tile | wns/raw
                request.Headers.Add("X-WNS-Type", "wns/toast");
                // 設置 access-token
                request.Headers.Add("Authorization", String.Format("Bearer {0}", token.AccessToken));

                // 須要推送的 toast 通知的內容
                string toastXml = $@"
                    <toast activationType='foreground' launch='PushNotification-Toast-Arguments'>
                        <visual>
                            <binding template='ToastGeneric'>
                                <text>toast - title</text>
                                <text>toast - content {DateTime.Now.ToString("mm:ss")}</text>
                            </binding>
                        </visual>
                    </toast>";

                // toast, tile, badge 爲 text/xml; raw 爲 application/octet-stream
                request.Content = new StringContent(toastXml, Encoding.UTF8, "text/xml");

                HttpResponseMessage response = await httpClient.SendAsync(request);
                /*
                 * 響應代碼說明
                 *     200 - OK,WNS 已接收到通知
                 *     400 - 錯誤的請求
                 *     401 - 未受權,token 可能無效
                 *     403 - 已禁止,manifest 中的 identity 可能不對
                 *     404 - 未找到
                 *     405 - 方法不容許
                 *     406 - 沒法接受
                 *     410 - 不存在,信道不存在或過時
                 *     413 - 請求實體太大,限制爲 5000 字節
                 *     500 - 內部服務器錯誤
                 *     503 - 服務不可用
                 */
                HttpStatusCode statusCode = response.StatusCode;
                result = new HttpResponseMessage
                {
                    Content = new StringContent(statusCode.ToString(), Encoding.UTF8, "text/html")
                };
            }
            catch (Exception ex)
            {
                result = new HttpResponseMessage
                {
                    Content = new StringContent(ex.ToString(), Encoding.UTF8, "text/html"),
                    StatusCode = HttpStatusCode.InternalServerError
                };
            }

            return result;
        }
    }



    /*
     * 用於反序列化從 https://login.live.com/accesstoken.srf 獲取到的結果
     */
    [DataContract]
    public class OAuthToken
    {
        [DataMember(Name = "access_token")]
        public string AccessToken { get; set; }
        [DataMember(Name = "token_type")]
        public string TokenType { get; set; }
    }



    /*
     * 用於從 https://login.live.com/accesstoken.srf 作 OAuth 驗證的幫助類
     */
    public class OAuthHelper
    {
        /// <summary>
        /// 獲取 https://login.live.com/accesstoken.srf 的 OAuth 驗證的 access-token
        /// </summary>
        /// <param name="secret">在商店後臺的 dashboard 中的「Application Secrets」中能夠找到此值(能夠在 https://apps.dev.microsoft.com/ 中查找)</param>
        /// <param name="sid">在商店後臺的 dashboard 中的「Package SID」中能夠找到此值(能夠在 https://apps.dev.microsoft.com/ 中查找)</param>
        /// <returns></returns>
        public OAuthToken GetAccessToken(string secret, string sid)
        {
            var urlEncodedSecret = UrlEncode(secret);
            var urlEncodedSid = 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 (WebClient 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);
        }

        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;
            }
        }

        private static string UrlEncode(string str)
        {
            StringBuilder sb = new StringBuilder();
            byte[] byStr = System.Text.Encoding.UTF8.GetBytes(str);
            for (int i = 0; i < byStr.Length; i++)
            {
                sb.Append(@"%" + Convert.ToString(byStr[i], 16));
            }

            return (sb.ToString());
        }
    }
}

BackgroundTask/PushNotification.xamlweb

<Page
    x:Class="Windows10.BackgroundTask.PushNotification"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.BackgroundTask"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="10 0 10 10">

            <TextBlock Name="lblMsg" Margin="5" />

            <Button Name="btnCreateChannel" Content="create the channel" Margin="5" Click="btnCreateChannel_Click" />

            <TextBox Name="txtUri" Margin="5" />

            <TextBlock Margin="5">
                <Run>一、應用爲推送通知通道向通知客戶端平臺發送請求。</Run>
                <LineBreak />
                <Run>二、通知客戶端平臺要求 WNS 建立通知通道。此通道以統一資源標識符 (URI) 的形式返回到調用設備。</Run>
                <LineBreak />
                <Run>三、通知通道 URI 由 Windows 返回到應用。</Run>
                <LineBreak />
                <Run>四、你的應用將 URI 發送到你本身的雲服務。此回調機制是你本身的應用和你本身的服務之間的接口。使用安全的 Web 標準實現此回調是你的責任。</Run>
                <LineBreak />
                <Run>五、當你的雲服務有要發送的更新時,它使用通道 URI 通知 WNS。經過安全套接字層 (SSL) 發送 TTP POST 請求(包括通知負載)來執行此操做。此步驟須要身份驗證。</Run>
                <LineBreak />
                <Run>六、WNS 接收請求,並將通知路由到相應的設備。</Run>
            </TextBlock>
            <Image Source="wns.png" Margin="5" HorizontalAlignment="Left" Width="800" />

        </StackPanel>
    </Grid>
    
</Page>

BackgroundTask/PushNotification.xaml.csexpress

/*
 * 演示如何接收推送通知
 * 因爲本例沒有上商店,因此本例是沒法演示的,須要看演示效果的話運行一下本身寫的「打字通」的 /TypingGame/PushNotification/Sample.xaml,而後用其生成的 channel 地址在 /WebApi/Controllers/PushNotificationController.cs 推送通知
 * 
 * 
 * 注:
 * 一、在商店後臺的 dashboard 中找到你的 app 的「包名」和「發佈者」並替換你的 Package.appxmanifest 中的相關節點,相似以下
 *    <Identity Name="10437webabcd.**********E91" Publisher="CN=27514DEC-****-****-****-F956384483D0" Version="1.0.0.0" />
 *    也能夠直接訪問 https://apps.dev.microsoft.com/ 來查找這些信息
 *    最簡單也是推薦的作法是:「選中項目」->「右鍵」->「應用商店」->「將應用程序與應用商店關聯」,按提示操做後會自動將商店信息同步到你的項目中
 * 二、須要在 Package.appxmanifest 中增長後臺任務聲明,並勾選「推送通知」(經測試發現不作這一步也能夠,可是爲了保險仍是加上吧)
 * 三、每次新建的 channel 有效期爲 30 天
 * 
 * 
 * 另:
 * WNS - Windows Push Notification Service
 * 推送通知的服務端參見:/WebApi/Controllers/PushNotificationController.cs
 */

using System;
using Windows.Networking.PushNotifications;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Windows10.BackgroundTask
{
    public sealed partial class PushNotification : Page
    {
        public PushNotification()
        {
            this.InitializeComponent();
        }

        private async void btnCreateChannel_Click(object sender, RoutedEventArgs e)
        {
            // 建立一個推送通知信道,每一個新建的 channel 有效期爲 30 天,因此建議每次進入 app 後都從新建一個 channel(若是兩次建立的間隔時間較短的話,則會複用以前的 channel 地址)
            PushNotificationChannel channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
            // 接收到通知後所觸發的事件
            channel.PushNotificationReceived += channel_PushNotificationReceived;

            // channel.Close(); // 關閉 channel
            // channel.ExpirationTime; // channel 的過時時間,此時間事後 channel 則失效

            // channel 的 uri 地址,服務端經過此 uri 向此 app 推送通知
            txtUri.Text = channel.Uri.ToString();
        }

        void channel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
        {
            switch (args.NotificationType)
            {
                case PushNotificationType.Badge: // badge 通知
                    BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(args.BadgeNotification);
                    break;
                case PushNotificationType.Raw: // raw 通知
                    // 當收到推送的 raw 通知時,若是 app 在鎖屏,則能夠觸發後臺任務以執行相關的邏輯(PushNotificationTrigger)
                    string msg = args.RawNotification.Content;
                    break;
                case PushNotificationType.Tile: // tile 通知
                    TileUpdateManager.CreateTileUpdaterForApplication().Update(args.TileNotification);
                    break;
                case PushNotificationType.Toast: // toast 通知
                    ToastNotificationManager.CreateToastNotifier().Show(args.ToastNotification);
                    break;
                default:
                    break;
            }

            args.Cancel = true;
        }
    }
}



OK
[源碼下載]json

相關文章
相關標籤/搜索