撸主是一个新手,最近几天在研究微信服务号交互,上网搜了搜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.首先是定义个消息类

     /// <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的更多相关文章

  1. 微信开发之Ngrok环境准备

    一.为什么要使用ngrok? 各位肯定都知道,做微信开发,我们的开发服务器需要和微信服务器做交互,SO,我们需要准备一台放置在公网的服务器,能够使得我们的服务器可以正常访问微信服务器,并且微信服务器也 ...

  2. 微信开发之Author网页授权

     微信开发中,经常有这样的需求:获得用户头像.绑定微信号给用户发信息.. 那么实现这些的前提就是授权!   1.配置安全回调域名: 在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的&q ...

  3. 微信开发之Ngrok环境准备(一)

    一.为什么要使用ngrok? 各位肯定都知道,做微信开发,我们的开发服务器需要和微信服务器做交互,SO,我们需要准备一台放置在公网的服务器,能够使得我们的服务器可以正常访问微信服务器,并且微信服务器也 ...

  4. thinkphp微信开发之jssdk图片上传并下载到本地服务器

    public function test2(){ $Weixin = new \Weixin\Controller\BaseController(); $this->assign('signPa ...

  5. 微信开发之SVN提交代码与FTP同步到apache的根目录

    SVN是协同开发的,版本控制器,就是几个人同时开发,可以提交代码到SVN服务器,这样就可以协同开发,一般是早上上班首先更新下代码,然后自己修改代码 工作一天之后,修改代码之后,下班之前,更新代码,然后 ...

  6. 微信开发之c#下jssdk签名生成

    参考文章 :微信JS-SDK 权限签名算法 C#版 这篇文章讲解的的比较详细,而且算法准确,但是这篇文章有几个错误的地方需要注意; url必须动态生成 url不能写死,否则就算结果和官方检测的一致,也 ...

  7. 微信开发之c#下获取jssdk的access_token

    获取access_token是调用微信JS接口的基础,只有得到了它才能得到我们需要的jsapi_ticket并生成签名,然后注入配置信息config. 微信官方文档我就不多做介绍,反正我是踩了不少坑. ...

  8. 微信开发之SSM环境搭建

    首先,感谢大神的文章,地址:http://blog.csdn.net/zhshulin/article/details/37956105# 第一步:新建maven项目 如有需要,查看之前的文章:从配置 ...

  9. 微信开发之web开发者工具

    web开发者工具. 有任何疑问或建议请私信我,或者在评论区大家一起探讨. 概述 为帮助开发者更方便.更安全地开发和调试基于微信的网页,我们推出了 web 开发者工具.它是一个桌面应用,通过模拟微信客户 ...

随机推荐

  1. blogilo在chinaunix发布博客的设置

    1. 在日志类型菜单中选择"Metaweblog API". 2. 在日志的远程发布url中输入"http://blog.chinaunix.net/xmlrpc.php ...

  2. Task Runner Explorer for vs2015找不到啊

    https://visualstudiogallery.msdn.microsoft.com/8e1b4368-4afb-467a-bc13-9650572db708/view/ 编译typescri ...

  3. 更高效地提高redis client多线程操作的并发吞吐设计

    Redis是一个非常高效的基于内存的NOSQL数据库,它提供非常高效的数据读写效能.在实际应用中往往是带宽和CLIENT库读写损耗过高导致无法更好地发挥出Redis更出色的能力.下面结合一些redis ...

  4. 在.net中使用aquiles访问Cassandra(三)

    之前我们实现了如何修改数据,还需要相应的删除动作.删除方式会有几种情况,以下分别一一介绍.   1.批量删除,适应于多行多列的情况. public void Remove(string columnF ...

  5. json-smart 使用示例(推荐fastjson)

    关于json库,请使用fastjson,这是我用过的最好用的json库! 地址:https://github.com/alibaba/fastjson ======================== ...

  6. [nRF51822] 4、 图解nRF51 SDK中的Schedule handling library 和Timer library

    :nRF51822虽然是一个小型的单片机,但是能真正达到任意调用其官方驱动以及BLE协议栈的人还是奇缺的.据我所见,大都拿官方给的一个冗长的蓝牙低功耗心率计工程改的.之前我对于这个工程进行log跟踪, ...

  7. C# 版 flvmerge:快速合并多个flv文件

    网上的视频很多都是分片的flv文件,怎么把他们合为一体呢?GUI工具就不考虑了,不适合批量执行,不适合在后台运行.有没有命令行工具或库可以实现呢? ffmpeg 提供了一个方法: (1)先把flv文件 ...

  8. jQuery 绑定事件到动态创建的元素上

    在进入主题之前,我们先来看一个前台页面经常用到的功能:点击页面输入框时自动选择其中文本. 很容易想到利用输入框的focus事件,当输入框获得焦点时,再调用jQuery的select()方法. Okay ...

  9. git 修改管理

    查看修改: 撤销某一文件的修改(还没提交): 撤销所有文件的修改: git checkout .

  10. EF架构~为分组添加位运算聚合方法

    回到目录 我们知道在Linq里的分组groupby可以对集合中一个或者多个字段进行分组,并对其中一个属性进行聚合,而Linq为我们提供了多种聚合方法,由aver,sum,count等,而在大叔权限体系 ...