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

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

最新文章

  1. HTML Basic Document and UML
  2. “双十一”购物狂欢节,电商运营和商业智能(BI)才是绝配
  3. 设置iframe背景透明
  4. 【Bzoj 3295】 动态逆序对(树套树|CDQ分治)
  5. Apache配置完虚拟主机后,使用Chrome访问localhost还是默认目录htdocs
  6. 代码对齐 分类: C#小技巧 2014-04-17 14:45 166人阅读 评论(0) 收藏
  7. django开发简易博客(五)
  8. C语言面试问答(3)
  9. vim 替换
  10. 探索 Java 热部署
  11. ubuntu14.04安装cuda
  12. POJ-1032-拆数字
  13. leetcode 54. Spiral Matrix 、59. Spiral Matrix II
  14. 【MySQL】MySQL内连接,左连接,右连接查询
  15. Vim怎么批量处理文件将tab变为space
  16. 读书笔记之Linux系统编程与深入理解Linux内核
  17. &lt;table&gt;居中的一种方法
  18. Uncontrolled memory mapping in camera driver (CVE-2013-2595)
  19. angular学习笔记(三十一)-$location(1)
  20. linux两种类型服务管理

热门文章

  1. 使用Code::blocks在windows下写网络程序
  2. 【转】各种语言中的urlencode方法
  3. maven配置发布仓库
  4. 微信公共平台开发-(.net实现)2--ACCESSTOKEN值获得
  5. ubuntu笔记
  6. MySQL1:MySQL函数汇总
  7. require、module、exports dojo中的三个特殊模块标识
  8. jQuery的extend方法的深层拷贝
  9. 使用SqlBulkCopy类来批量复制数据
  10. MVVM架构~knockoutjs系列之级联select