微信开发之.Net
撸主是一个新手,最近几天在研究微信服务号交互,上网搜了搜C#的代码,再结合自己的习惯,下面把代码解析一下,其中有些代码非本人原创。
首先,你要有个公众服务号,只有服务号才可以自定义菜单,没有条件的可以申请订阅号,然后再申请测试服务号。
微信调用服务端的接口其实分为2个部分,第一,验证此消息是否是微信发出来的,这通过get参数获取,像这样"?signature=eebf87348f23a73debd0e8a4235bb4e798099365&echostr=5964279561876822008×tamp=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.首先是定义个消息类
- /// <summary>
- /// 消息的父类
- /// </summary>
- public abstract class Message
- {
- /// <summary>
- /// 收信人
- /// </summary>
- public string ToUserName { get; set; }
- /// <summary>
- /// 发信人
- /// </summary>
- public string FromUserName { get; set; }
- /// <summary>
- /// 创建时间
- /// </summary>
- public long CreateTime { get; set; }
- /// <summary>
- /// 消息类型
- /// </summary>
- public string MsgType { get; set; }
- /// <summary>
- /// 消息的类型:Send接收的消息,Reply发送的消息
- /// </summary>
- public MessageDirection Direction { get; set; }
- /// <summary>
- /// 将xml文档转换为具体的接收实例
- /// </summary>
- /// <param name="node"></param>
- /// <returns></returns>
- protected abstract Message Parse(XmlNode node);
- public override string ToString()
- {
- return string.Empty;
- }
- /// <summary>
- /// 得到具体的消息处理实例
- /// </summary>
- /// <param name="xmlStream"></param>
- /// <returns></returns>
- public static Message GetInstance(Stream xmlStream)
- {
- if (xmlStream == null || xmlStream.Length == 0)
- {
- return null;
- }
- //得到请求的内容
- byte[] bytes = new byte[xmlStream.Length];
- xmlStream.Read(bytes, 0, (int)xmlStream.Length);
- string xml = Encoding.UTF8.GetString(bytes);
- return GetInstance(xml);
- }
- /// <summary>
- /// 得到具体的消息处理实例
- /// </summary>
- /// <param name="xml"></param>
- /// <returns></returns>
- public static Message GetInstance(string xml)
- {
- XmlDocument doc = new XmlDocument();
- Message message = null;
- try
- {
- doc.LoadXml(xml);
- XmlNode firstNode = doc.FirstChild;
- if (firstNode == null)
- {
- return null;
- }
- //消息类型
- XmlNode tempNode = firstNode.SelectSingleNode("MsgType");
- if (tempNode == null)
- {
- return null;
- }
- message = GetInstance(tempNode);
- if (message != null)
- {
- return message.Parse(doc.FirstChild);
- }
- return message;
- }
- catch (Exception ex)
- {
- return null;
- }
- }
- private static Message GetInstance(XmlNode node)
- {
- switch (node.InnerText)
- {
- case MessageType.Text: return new TextSendMessage();
- case MessageType.Image: return null;
- case MessageType.Link: return null;
- case MessageType.Location: return null;
- case MessageType.Music: return null;
- case MessageType.Video: return null;
- case MessageType.Voice: return null;
- case MessageType.Event: return new EventSendMessage();
- default: return null;
- }
- }
- }
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);
- }
- }
然后是回复的:
- 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);
- }
- }
接收的事件信息
- 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 = "无效的指令请求!"
- };
- }
- }
然后是处理回发数据的父类
- /// <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);
- }
我只弄了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 = ""
- };
- }
- }
事件:
- /// <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 = ""
- };
- }
- }
具体的逻辑自己控制。
这样,整体就基本完成了。
下面是调用:
- /// <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 + "\r\n", "uri.txt");
- }
- var stream = context.Request.InputStream;
- string result = null;
- if (stream != null && stream.Length > )
- {
- var bytes = new byte[stream.Length];
- stream.Read(bytes, , (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 == )
- {
- 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 == )
- {
- _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);
- }
- }
- }
- }
大概就这样了,其实处理流程很简单,就是 验证-->通过的话告诉微信--> 接收消息,判断是什么类型的数据-->发给微信-->微信会根据ToUserName推送给指定的用户。
下面是代码附件:
http://files.cnblogs.com/pokemon/WeiXinDemo.rar
小弟只是一个新手,大家多交流才能进步!
微信开发之.Net的更多相关文章
- 微信开发之Ngrok环境准备
一.为什么要使用ngrok? 各位肯定都知道,做微信开发,我们的开发服务器需要和微信服务器做交互,SO,我们需要准备一台放置在公网的服务器,能够使得我们的服务器可以正常访问微信服务器,并且微信服务器也 ...
- 微信开发之Author网页授权
微信开发中,经常有这样的需求:获得用户头像.绑定微信号给用户发信息.. 那么实现这些的前提就是授权! 1.配置安全回调域名: 在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的&q ...
- 微信开发之Ngrok环境准备(一)
一.为什么要使用ngrok? 各位肯定都知道,做微信开发,我们的开发服务器需要和微信服务器做交互,SO,我们需要准备一台放置在公网的服务器,能够使得我们的服务器可以正常访问微信服务器,并且微信服务器也 ...
- thinkphp微信开发之jssdk图片上传并下载到本地服务器
public function test2(){ $Weixin = new \Weixin\Controller\BaseController(); $this->assign('signPa ...
- 微信开发之SVN提交代码与FTP同步到apache的根目录
SVN是协同开发的,版本控制器,就是几个人同时开发,可以提交代码到SVN服务器,这样就可以协同开发,一般是早上上班首先更新下代码,然后自己修改代码 工作一天之后,修改代码之后,下班之前,更新代码,然后 ...
- 微信开发之c#下jssdk签名生成
参考文章 :微信JS-SDK 权限签名算法 C#版 这篇文章讲解的的比较详细,而且算法准确,但是这篇文章有几个错误的地方需要注意; url必须动态生成 url不能写死,否则就算结果和官方检测的一致,也 ...
- 微信开发之c#下获取jssdk的access_token
获取access_token是调用微信JS接口的基础,只有得到了它才能得到我们需要的jsapi_ticket并生成签名,然后注入配置信息config. 微信官方文档我就不多做介绍,反正我是踩了不少坑. ...
- 微信开发之SSM环境搭建
首先,感谢大神的文章,地址:http://blog.csdn.net/zhshulin/article/details/37956105# 第一步:新建maven项目 如有需要,查看之前的文章:从配置 ...
- 微信开发之web开发者工具
web开发者工具. 有任何疑问或建议请私信我,或者在评论区大家一起探讨. 概述 为帮助开发者更方便.更安全地开发和调试基于微信的网页,我们推出了 web 开发者工具.它是一个桌面应用,通过模拟微信客户 ...
随机推荐
- spring核心框架体系结构
很多人都在用spring开发java项目,但是配置maven依赖的时候并不能明确要配置哪些spring的jar,经常是胡乱添加一堆,编译或运行报错就继续配置jar依赖,导致spring依赖混乱,甚至下 ...
- Linux(CentOS 6.5)下配置Mono和Jexus并且部署ASP.NET MVC5
1.开篇说明 a. 首先我在写这篇博客之前,已经在自己本地配置了mono和jexus并且成功部署了asp.net mvc项目,我也是依赖于在网上查找的各种资料来配置环境并且部署项目的,而其在网上也已有 ...
- centos install kafka and zookeeper
1.安装zookeeper ZooKeeper is a distributed, open-source coordination service for distributed applicati ...
- js中的console很强大
今天闲来没事,瞎逛, 发现了淘宝首页的这个: 想起来之前在百度的 页面中也曾看到过.于是乎自己试一试. 于是便见识到了console对象其实很强大,用好它对调试真的有很大帮助. 代码: <!DO ...
- appserv中php升级问题
当前版本为2.1,要升级到2.3.4 那么,首先到http://windows.php.net/downloads/releases/archives/ 找到2.3.4,需要注意的是,一般我们是非nt ...
- Linux下Java开发环境搭建—CentOS下Mysql安装教程
本人学习Linux时使用的是CentOs5.5版本,在该环境中,Mysql的安装方法有很多种,下面我只讲我这次成功了的方法,作为一个记录,供大家参考,同时给自己做一个笔记. MySQL下载 1.进入网 ...
- 虚拟化平台cloudstack(8)——从UI开始
UI ucloudstack采用的是前后端分离的架构,就是说前端可以选择使用web.swing甚至其它的界面,都可以. 我们来看cloudstack的UI信息吧,所有的cloudstack的UI都在{ ...
- sql的OUTER APPLY
今天写一个查询sql,其中涉及到一个银行卡绑定表(表名:BankBind),我要将这个表的开户行字段的值进行分割出省份.支行, 这个开户行字段存储的值如“广东省广东省分行江门市分行恩平市支行”.“招商 ...
- Oracle建表脚本记录
--删除 drop table dianfei; --创建表 create table dianfei ( uon ) not null, mmonth ) not null, ddf ,) not ...
- DDD~领域事件应用篇(订单处理变得更清晰)
回到目录 上一讲主要说了领域事件和领域总线,这并不是一个很容易理解的文章,所以本讲实例篇主要是为了补充上一讲的理论知识,本讲实例关注的是实际中的订单处理模块,我们知道,订单处理是电子商务的核心,往往在 ...