代码改变世界
[登录 · 注册]
  • 背水一战 Windows 10 (121) 后台任务: 推送通知
  • [源码下载]


    背水一战 Windows 10 (121) - 后台任务: 推送通知



    作者:webabcd


    介绍
    背水一战 Windows 10 之 后台任务

    • 推送通知



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

    /*
     * 演示如何向 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.xaml

    <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>1、应用为推送通知通道向通知客户端平台发送请求。</Run>
                    <LineBreak />
                    <Run>2、通知客户端平台要求 WNS 创建通知通道。此通道以统一资源标识符 (URI) 的形式返回到调用设备。</Run>
                    <LineBreak />
                    <Run>3、通知通道 URI 由 Windows 返回到应用。</Run>
                    <LineBreak />
                    <Run>4、你的应用将 URI 发送到你自己的云服务。此回调机制是你自己的应用和你自己的服务之间的接口。使用安全的 Web 标准实现此回调是你的责任。</Run>
                    <LineBreak />
                    <Run>5、当你的云服务有要发送的更新时,它使用通道 URI 通知 WNS。通过安全套接字层 (SSL) 发送 TTP POST 请求(包括通知负载)来执行此操作。此步骤需要身份验证。</Run>
                    <LineBreak />
                    <Run>6、WNS 接收请求,并将通知路由到相应的设备。</Run>
                </TextBlock>
                <Image Source="wns.png" Margin="5" HorizontalAlignment="Left" Width="800" />
    
            </StackPanel>
        </Grid>
        
    </Page>

    BackgroundTask/PushNotification.xaml.cs

    /*
     * 演示如何接收推送通知
     * 由于本例没有上商店,所以本例是无法演示的,需要看演示效果的话运行一下自己写的“打字通”的 /TypingGame/PushNotification/Sample.xaml,然后用其生成的 channel 地址在 /WebApi/Controllers/PushNotificationController.cs 推送通知
     * 
     * 
     * 注:
     * 1、在商店后台的 dashboard 中找到你的 app 的“包名”和“发布者”并替换你的 Package.appxmanifest 中的相关节点,类似如下
     *    <Identity Name="10437webabcd.**********E91" Publisher="CN=27514DEC-****-****-****-F956384483D0" Version="1.0.0.0" />
     *    也可以直接访问 https://apps.dev.microsoft.com/ 来查找这些信息
     *    最简单也是推荐的做法是:“选中项目”->“右键”->“应用商店”->“将应用程序与应用商店关联”,按提示操作后会自动将商店信息同步到你的项目中
     * 2、需要在 Package.appxmanifest 中增加后台任务声明,并勾选“推送通知”(经测试发现不做这一步也可以,但是为了保险还是加上吧)
     * 3、每次新建的 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
    [源码下载]

  • 【推广】 阿里云小站-上云优惠聚集地(新老客户同享)更有每天限时秒杀!
    【推广】 云服务器低至0.95折 1核2G ECS云服务器8.1元/月
    【推广】 阿里云老用户升级四重礼遇享6.5折限时折扣!
  • 原文:https://www.cnblogs.com/webabcd/p/9211779.html
走看看 - 开发者的网上家园