微信开发之.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

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

原文地址:https://www.cnblogs.com/pokemon/p/3510286.html