zoukankan      html  css  js  c++  java
  • 微信开发之.Net

      撸主是一个新手,最近几天在研究微信服务号交互,上网搜了搜C#的代码,再结合自己的习惯,下面把代码解析一下,其中有些代码非本人原创。

      首先,你要有个公众服务号,只有服务号才可以自定义菜单,没有条件的可以申请订阅号,然后再申请测试服务号。

      微信调用服务端的接口其实分为2个部分,第一,验证此消息是否是微信发出来的,这通过get参数获取,像这样"?signature=eebf87348f23a73debd0e8a4235bb4e798099365&echostr=5964279561876822008&timestamp=1388992504&nonce=1388667053",其中signature是验证签名,我们要做的就是把提交微信网站的token,timestamp和nonce参数通过计算签名与signature参数比对,如果正确则证明这个是微信官方的信息,具体的代码是这样的。

      

     public static bool VerifySignature(string token, string signature, string timeStamp, string nonce)
            {
                //基本检查
                if (string.IsNullOrEmpty(signature))
                {
                    return false;
                }
                //签名验证
                string[] stringArray = { token, timeStamp, nonce };
                //排序
                Array.Sort(stringArray);
                //计算签名
                string calculatedSignature = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(
                    string.Join(string.Empty, stringArray), System.Web.Configuration.FormsAuthPasswordFormat.SHA1.ToString());
    
                return calculatedSignature != null && calculatedSignature.ToLower() == signature;
            }

      如果验证通过的话一定要给微信返回信号,否则微信会认为无响应的。!

      /// <summary>
            /// 告知微信服务器,已通过验证
            /// </summary>
            public static void EchoPass()
            {
                //响应微信验证
                HttpContext.Current.Response.Write(HttpContext.Current.Request["echostr"]);
                HttpContext.Current.Response.Flush();
            }

    其中参数echostr是微信服务器随机生成的,直接返回即可。

      响应完成后就需要我们的应用程序来处理信息了,首先必须判断返回来的是哪种信息。

      微信的信息种类有: 文本消息:text , 图片消息:image ,事件消息:event , 语音消息:voice ,视频消息: video ,地理位置消息:location ,链接消息:link 

      消息主体就是第二部分了,内容在Request.InputStream中,需要解析成xml文件,xml格式大概是这样的:

        

    <xml><ToUserName><![CDATA[收件人]]></ToUserName>
    <FromUserName><![CDATA[发件人]]></FromUserName>
    <CreateTime>1389084129</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[CLICK]]></Event>
    <EventKey><![CDATA[ceshi1]]></EventKey>
    </xml>

    上面的是一个事件的消息,事件就是点击服务号的自定义菜单。

    接下来就是解析xml成具体的实例了,关于消息的处理我是这样解决的(一部分借鉴别人给的代码,再结合自己的习惯):

    1.首先是定义个消息类

      1     /// <summary>
      2     /// 消息的父类
      3     /// </summary>
      4     public abstract class Message
      5     {
      6         /// <summary>
      7         /// 收信人
      8         /// </summary>
      9         public string ToUserName { get; set; }
     10         /// <summary>
     11         /// 发信人
     12         /// </summary>
     13         public string FromUserName { get; set; }
     14         /// <summary>
     15         /// 创建时间
     16         /// </summary>
     17         public long CreateTime { get; set; }
     18         /// <summary>
     19         /// 消息类型
     20         /// </summary>
     21         public string MsgType { get; set; }
     22 
     23         /// <summary>
     24         /// 消息的类型:Send接收的消息,Reply发送的消息
     25         /// </summary>
     26         public MessageDirection Direction { get; set; }
     27         /// <summary>
     28         /// 将xml文档转换为具体的接收实例
     29         /// </summary>
     30         /// <param name="node"></param>
     31         /// <returns></returns>
     32         protected abstract Message Parse(XmlNode node);
     33 
     34         public override string ToString()
     35         {
     36             return string.Empty;
     37         }
     38 
     39         /// <summary>
     40         /// 得到具体的消息处理实例
     41         /// </summary>
     42         /// <param name="xmlStream"></param>
     43         /// <returns></returns>
     44         public static Message GetInstance(Stream xmlStream)
     45         {
     46             if (xmlStream == null || xmlStream.Length == 0)
     47             {
     48                 return null;
     49             }
     50             //得到请求的内容
     51             byte[] bytes = new byte[xmlStream.Length];
     52             xmlStream.Read(bytes, 0, (int)xmlStream.Length);
     53             string xml = Encoding.UTF8.GetString(bytes);
     54             return GetInstance(xml);
     55         }
     56         /// <summary>
     57         /// 得到具体的消息处理实例
     58         /// </summary>
     59         /// <param name="xml"></param>
     60         /// <returns></returns>
     61         public static Message GetInstance(string xml)
     62         {
     63             XmlDocument doc = new XmlDocument();
     64             Message message = null;
     65             try
     66             {
     67                 doc.LoadXml(xml);
     68                 XmlNode firstNode = doc.FirstChild;
     69                 if (firstNode == null)
     70                 {
     71                     return null;
     72                 }
     73                 //消息类型
     74                 XmlNode tempNode = firstNode.SelectSingleNode("MsgType");
     75                 if (tempNode == null)
     76                 {
     77                     return null;
     78                 }
     79                 message = GetInstance(tempNode);
     80                 if (message != null)
     81                 {
     82                     return message.Parse(doc.FirstChild);
     83                 }
     84                 return message;
     85             }
     86             catch (Exception ex)
     87             {
     88                 return null;
     89             }
     90         }
     91 
     92         private static Message GetInstance(XmlNode node)
     93         {
     94             switch (node.InnerText)
     95             {
     96                 case MessageType.Text: return new TextSendMessage();
     97                 case MessageType.Image: return null;
     98                 case MessageType.Link: return null;
     99                 case MessageType.Location: return null;
    100                 case MessageType.Music: return null;
    101                 case MessageType.Video: return null;
    102                 case MessageType.Voice: return null;
    103                 case MessageType.Event: return new EventSendMessage();
    104                 default: return null;
    105             }
    106         }
    107 
    108     }
    View Code

    2.具体的消息类型,我就弄了个文本和事件,首先是文本: 

        /// <summary>
        /// 处理消息类
        /// </summary>
        public abstract class TextMessage : Message
        {
            public TextMessage()
            {
                this.MsgType = MessageType.Text;
            }
            public string Content { get; set; }
    
            protected override Message Parse(System.Xml.XmlNode node)
            {
                return null;
            }
        
        }

    消息分为接收的和回复的,先是接收的:

       /// <summary>
        /// 处理消息类
        /// </summary>
        public class TextSendMessage : TextMessage
        {
            public TextSendMessage()
            {
                this.Direction = MessageDirection.Send;
            }
    
            public string MsgId { get; set; }
    
            protected override Message Parse(XmlNode node)
            {
                //发送者
                XmlNode tempNode = node.SelectSingleNode("FromUserName");
                if (tempNode == null)
                {
                    return null;
                }
                this.FromUserName = tempNode.InnerText;
                //接收者
                tempNode = node.SelectSingleNode("ToUserName");
                if (tempNode == null)
                {
                    return null;
                }
                this.ToUserName = tempNode.InnerText;
                //创建时间
                tempNode = node.SelectSingleNode("CreateTime");
                if (tempNode == null)
                {
                    return null;
                }
                this.CreateTime = Convert.ToInt64(tempNode.InnerText);
    
                //消息内容
                tempNode = node.SelectSingleNode("Content");
                if (tempNode == null)
                {
                    return null;
                }
                this.Content = tempNode.InnerText;
    
                //消息ID
                tempNode = node.SelectSingleNode("MsgId");
                if (tempNode == null)
                {
                    return null;
                }
                this.MsgId = tempNode.InnerText;
    
                return this;
            }
    
            public override string ToString()
            {
                return string.Format("<xml>" + Environment.NewLine +
                                    "<ToUserName><![CDATA[{0}]]></ToUserName>" + Environment.NewLine +
                                    "<FromUserName><![CDATA[{1}]]></FromUserName>" + Environment.NewLine +
                                    "<CreateTime>{2}</CreateTime>" + Environment.NewLine +
                                    "<MsgType><![CDATA[{3}]]></MsgType>" + Environment.NewLine +
                                    "<Content><![CDATA[{4}]]></Content>" + Environment.NewLine +
                                    "<MsgId>{5}</MsgId>" + Environment.NewLine +
                                    "</xml>", ToUserName, FromUserName, CreateTime, MsgType, Content, MsgId);
            }
        }
    View Code

    然后是回复的:

        public class TextReplyMessage:TextMessage
        {
            public TextReplyMessage()
            {
                this.Direction = MessageDirection.Reply;
            }
            public string FuncFlag { get; set; }
    
            public override string ToString()
            {
                return string.Format("<xml>" + Environment.NewLine +
                                     "<ToUserName><![CDATA[{0}]]></ToUserName>" + Environment.NewLine +
                                     "<FromUserName><![CDATA[{1}]]></FromUserName>" + Environment.NewLine +
                                     "<CreateTime>{2}</CreateTime>" + Environment.NewLine +
                                     "<MsgType><![CDATA[{3}]]></MsgType>" + Environment.NewLine +
                                     "<Content><![CDATA[{4}]]></Content>" + Environment.NewLine +
                                     "<FuncFlag>{5}</FuncFlag>" + Environment.NewLine +
                                     "</xml>", ToUserName, FromUserName, CreateTime, MsgType, Content, FuncFlag);
            }
        }

    接下来是事件处理:

        /// <summary>
        /// 事件的处理
        /// </summary>
        public abstract class EventMessage : Message
        {
            public EventMessage()
            {
                this.MsgType = MessageType.Event;
            }
    
            public string Event { get; set; }
            public string EventKey { get; set; }
    
            protected override Message Parse(XmlNode node)
            {
                //发送者
                XmlNode tempNode = node.SelectSingleNode("FromUserName");
                if (tempNode == null)
                {
                    return null;
                }
                FromUserName = tempNode.InnerText;
                //接收者
                tempNode = node.SelectSingleNode("ToUserName");
                if (tempNode == null)
                {
                    return null;
                }
                ToUserName = tempNode.InnerText;
                //创建时间
                tempNode = node.SelectSingleNode("CreateTime");
                if (tempNode == null)
                {
                    return null;
                }
                CreateTime = Convert.ToInt64(tempNode.InnerText);
    
                //事件(subscribe/unsubscribe/CLICK)
                tempNode = node.SelectSingleNode("Event");
                if (tempNode == null)
                {
                    return null;
                }
                Event = tempNode.InnerText;
    
                //事件Key(当Event=CLICK时,使用Key定位具体单击的是哪个菜单项)
                tempNode = node.SelectSingleNode("EventKey");
                if (tempNode == null)
                {
                    return null;
                }
                EventKey = tempNode.InnerText;
                return this;
            }
    
            public override string ToString()
            {
                return string.Format("<xml>" + Environment.NewLine +
                                    "<ToUserName><![CDATA[{0}]]></ToUserName>" + Environment.NewLine +
                                    "<FromUserName><![CDATA[{1}]]></FromUserName>" + Environment.NewLine +
                                    "<CreateTime>{2}</CreateTime>" + Environment.NewLine +
                                    "<MsgType><![CDATA[{3}]]></MsgType>" + Environment.NewLine +
                                    "<Event><![CDATA[{4}]]></Event>" + Environment.NewLine +
                                    "<EventKey>{5}</EventKey>" + Environment.NewLine +
                                    "</xml>", ToUserName, FromUserName, CreateTime, MsgType, Event, EventKey);
            }
        }
    View Code

    接收的事件信息

      public class EventSendMessage:EventMessage
        {
            public EventSendMessage()
            {
                this.Direction = MessageDirection.Send;
            }
        }

    现在就弄了这些,上面的只是接收微信的信息,有接收就得有处理回发数据。

    定义一个消息处理中心(这个是网上的代码。。。)

     /// <summary>
        /// 指令处理中心
        /// </summary>
        public class InstructionHandlingCenter
        {
            public List<Instruction> InstructionList { get; private set; }
            public InstructionHandlingCenter()
            {
                this.InstructionList = new List<Instruction>();
            }
            /// <summary>
            /// 指令注册
            /// </summary>
            /// <param name="instruction"></param>
            public void RegisterInstruction(Instruction instruction)
            {
                if (!this.InstructionList.Contains(instruction))
                {
                    this.InstructionList.Add(instruction);
                }
            }
    
            /// <summary>
            /// 根据请求获取返回消息
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            public Message GetReplyMessage(Message requestMessage)
            {
                foreach (var instruction in InstructionList)
                {
                    if (instruction.MatchWith(requestMessage))
                    {
                        return instruction.GetReplyInstance(requestMessage);
                    }
                }
                return new TextReplyMessage
                {
                    FromUserName = requestMessage.ToUserName,
                    ToUserName = requestMessage.FromUserName,
                    CreateTime = Convert.ToInt64(DateTime.Now.Ticks.ToString(System.Globalization.CultureInfo.InvariantCulture)),
                    Content = "无效的指令请求!"
                };
            }
    
        }
    View Code

    然后是处理回发数据的父类

     /// <summary>
        /// 处理回发数据
        /// </summary>
        public abstract class Instruction
        {
            public abstract string HelpMessage { get; }
            /// <summary>
            /// 检查指令是否与消息相匹配
            /// </summary>
            /// <param name="message"></param>
            /// <returns></returns>
            public abstract bool MatchWith(Message message);
    
            /// <summary>
            /// 分析消息内容并返回对应响应值
            /// </summary>
            /// <param name="message"></param>
            /// <returns></returns>
            public abstract Message GetReplyInstance(Message message);
        }
    View Code

    我只弄了2个,一个文本,一个事件

    文本:

     public class TextInstruction : Instruction
        {
            public override string HelpMessage
            {
                get
                {
                    return "欢迎关注..........";
                }
            }
    
            public override bool MatchWith(Message message)
            {
                return message is TextSendMessage;
            }
    
            public override Message GetReplyInstance(Message message)
            {
                return new TextReplyMessage()
                {
                    FromUserName = message.ToUserName,
                    ToUserName = message.FromUserName,
                    CreateTime = Convert.ToInt64(DateTime.Now.Ticks.ToString(System.Globalization.CultureInfo.InvariantCulture)),
                    Content = HelpMessage,
                    FuncFlag = "1" 
                };
            }
        }
    View Code

    事件:

      /// <summary>
        /// 事件处理
        /// </summary>
        public class EventInstruction : Instruction
        {
    
            private string _helMessage = "事件触发";
            public override string HelpMessage
            {
                get { return this._helMessage; }
            }
    
            public override bool MatchWith(Message message)
            {
                return message is EventSendMessage;
            }
    
            public override Message GetReplyInstance(Message message)
            {
                EventSendMessage esm = message as EventSendMessage;
                this._helMessage = esm.EventKey;
                return new TextReplyMessage()
                {
                    FromUserName = message.ToUserName,
                    ToUserName = message.FromUserName,
                    CreateTime = Convert.ToInt64(DateTime.Now.Ticks.ToString(System.Globalization.CultureInfo.InvariantCulture)),
                    Content = HelpMessage,
                    FuncFlag = "1"
                };
            }
        }
    View Code

    具体的逻辑自己控制。

    这样,整体就基本完成了。

    下面是调用:

     /// <summary>
        /// WeiXinApi 的摘要说明
        /// </summary>
        public class WeiXinApi : IHttpHandler
        {
            //微信服务号
            protected readonly string WeiXinServerNo = "???";
            //注册时指定的Token
            protected readonly string Token = "???";
    
    
            //指令处理中心
            private readonly InstructionHandlingCenter _instructionHandler = new InstructionHandlingCenter();
    
            public void ProcessRequest(HttpContext context)
            {
                Save("ok", "test.txt");
    
                Uri uri = context.Request.Url;
                if (uri != null)
                {
                    Save(uri.PathAndQuery + "
    ", "uri.txt");
                }
    
                var stream = context.Request.InputStream;
                string result = null;
                if (stream != null && stream.Length > 0)
                {
                    var bytes = new byte[stream.Length];
                    stream.Read(bytes, 0, (int)stream.Length);
                    result = Encoding.UTF8.GetString(bytes);
                    Save(result, "xml.txt");
                }
    
                //初始化变量
                InitializeVariables();
    
                //验证签名
                if (!WeiXinCommon.VerifySignature(Token))
                {
                    context.Response.Write("验证签名错误");
                    Save("验证签名错误", "result.txt");
                    return;
                }
    
    
                //响应微信验证
                WeiXinCommon.EchoPass();
    
                if (context.Request.InputStream == null || context.Request.InputStream.Length == 0)
                {
                    Save("InputStream没有", "result.txt");
                    return;
                }
    
                //获取指令请求
                Message requestMessage = Message.GetInstance(result);
    
                if (requestMessage == null)
                {
                    context.Response.Write("获取指令请求为null(requestMessage)");
                    Save("获取指令请求为null(requestMessage)", "result.txt");
                    return;
                }
    
                var replyMessage = _instructionHandler.GetReplyMessage(requestMessage);
                if (replyMessage == null)
                {
                    context.Response.Write("获取指令请求为null(replyMessage)");
                    Save("获取指令请求为null(replyMessage)", "result.txt");
                    return;
                }
                WeiXinCommon.SendReplyMessage(replyMessage);
                Save(replyMessage.ToString(), "result.txt");
    
            }
    
            public bool IsReusable
            {
                get
                {
                    return false;
                }
            }
    
    
            //初始化变量
            private void InitializeVariables()
            {
                //初始化可处理的指令列表
                if (_instructionHandler.InstructionList.Count == 0)
                {
                    _instructionHandler.RegisterInstruction(new TextInstruction());
                    _instructionHandler.RegisterInstruction(new EventInstruction());
                }
            }
    
            private void Save(string value, string fileName)
            {
                using (Stream stream = File.Open(HttpContext.Current.Server.MapPath(fileName), FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
                {
                    using (StreamWriter writer = new StreamWriter(stream, System.Text.Encoding.UTF8))
                    {
                        writer.WriteLine(value);
                    }
                }
            }
    
        }
    View Code

    大概就这样了,其实处理流程很简单,就是  验证-->通过的话告诉微信--> 接收消息,判断是什么类型的数据-->发给微信-->微信会根据ToUserName推送给指定的用户。

    下面是代码附件:

    https://files.cnblogs.com/pokemon/WeiXinDemo.rar

    小弟只是一个新手,大家多交流才能进步!

  • 相关阅读:
    抽象工厂学习笔记
    SQL Transcation的一些总结
    享元模式(Flyweight)
    编程语言简史
    代理模式(Proxy)
    打造属于你的提供者(Provider = Strategy + Factory Method)
    打造属于你的加密Helper类
    单例模式(Singleton)的6种实现
    在C#中应用哈希表(Hashtable)
    InstallShield脚本语言的编写
  • 原文地址:https://www.cnblogs.com/pokemon/p/3510286.html
Copyright © 2011-2022 走看看