撸主是一个新手,最近几天在研究微信服务号交互,上网搜了搜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. 记AbpSession扩展实现过程

    AbpSession只给了userId和TenantId,这次实际项目中并不够用,网上找了很久也没找到好的实现方法.项目初期没有时间进行研究,最近空了试了一下,大致实现添加额外字段并读取相应值的功能. ...

  2. SqlServer2012 数据库的同步之发布+订阅

    文章参考了百度过的文章,因为版本不同,操作中也遇到了很多问题,现在整理一下,希望对各位朋友有所帮助. 发布订阅份为两个步骤:1.发布.2订阅.首先在数据源数据库服务器上对需要同步的数据进行发布,然后在 ...

  3. C#中使用OpenSSL的公钥加密/私钥解密

    在C#中进行公钥加密/私钥解密,需要用RSACryptoServiceProvider,但是它不支持由OpenSSL生成的公钥/私钥字符串. 比如这样的公钥/私钥对( 公私钥生成方法见 http:// ...

  4. STC12C5A60S2笔记7(定时器)

    1. 基本特性 STC12C5A60S2单片机集成了两个16位定时/计数器. 1)寄存器 1.1)TMOD 定时器工作方式控制寄存器,包括13位寄存器.16位寄存器.8位寄存器等: 1.2)TCON ...

  5. 设计模式之美:Dynamic Property(动态属性)

    索引 别名 意图 结构 参与者 适用性 效果 实现 实现方式(一):Dynamic Property 的示例实现. 别名 Property Properties Property List 意图 使对 ...

  6. DBCP连接池使用问题

    问题现象: 启动应用,访问无压力,一切正常,一段时间过后,应用访问异常. 问题分析: 1.web容器线程爆满,拒绝服务.由于应用并发量大,线程响应时间长增加,线程池连接数逐步递增直到爆满,导致应用拒绝 ...

  7. JavaScript使用DeviceOne开发实战(二) 生成调试安装包

    生成调试安装包 首先需要说明的是,这个步骤并不是每次调试App都必须的,大部分情况生成一次调试安装包,安装到手机上之后就可以忽略整个这个步骤.因为调试安装包包含了很多原生组件,都是可以定制勾选的,如果 ...

  8. Flash 与 php 使用 amfphp

    创建 Flash 项目 使用 Flash Builder 创建一个项目. 创建 Flash 项目时,选择服务器技术为 PHP,并配置好服务器的 Web 根文件夹及根 URL 地址(这里设置根文件夹时, ...

  9. windows 8.1 试用感受:蛋疼感大幅降低

    众所周知windows 8 的最大使用感受就是蛋疼. 无论是微软MVP,还是我这样的万年不悔微软小白鼠,普通用户,小白用户,或多或少的都对这款操作系统感到蛋疼. 槽点太多,以至于大家都懒得批判了.好在 ...

  10. [蓝牙] 2、蓝牙BLE协议及架构浅析&&基于广播超时待机说广播事件

    第一章 BLE基本概念了解 一.蓝牙4.0和BLE区别   蓝牙4.0是一种应用非常广泛.基于2.4G射频的低功耗无线通讯技术.蓝牙低功耗(Bluetooth Low Energy ),人们又常称之为 ...