微信开发之.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 开发者工具.它是一个桌面应用,通过模拟微信客户 ...
随机推荐
- SQL Saturday活动再起
SQL Saturday活动再起 时间:2015年05月09日(星期六) 地点:上海徐汇区港汇2座10楼(10.073) 我们相约港汇2座10楼(10.073),SQL PASS上海分会的SQLSat ...
- SQL入门经典(四)之创建和修改数据表
本章主要讲如何使用脚本创建数据库:如何使用脚本创建表:如何删除对象和修改对象. CREATE 语句:CREATE <object type> <onject name> 创建数 ...
- nginx+winsw windows服务
1.下载Nginx:http://nginx.org/en/download.html 2.下载winsw配置包:http://files.cnblogs.com/files/objecttozero ...
- Java提高篇(三二)-----List总结
前面LZ已经充分介绍了有关于List接口的大部分知识,如ArrayList.LinkedList.Vector.Stack,通过这几个知识点可以对List接口有了比较深的了解了.只有通过归纳总结的知识 ...
- Java提高篇(二七)-----TreeMap
TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap的实现,但是为了与Java提高篇系列博文保持一致 ...
- 手把手教你用python打造网易公开课视频下载软件3-对抓取的数据进行处理
上篇讲到抓取的数据保存到rawhtml变量中,然后通过编码最终保存到html变量当中,那么html变量还会有什么问题吗?当然会有了,例如可能html变量中的保存的抓取的页面源代码可能有些标签没有关闭标 ...
- IOS UIView 04- 自定义控件
注:本人是翻译过来,并且加上本人的一点见解. 前言 本文将讨论一些自定义视图.控件的诀窍和技巧.我们先概述一下 UIKit 向我们提供的控件,并介绍一些渲染技巧.随后我们会深入到视图和其所有者之间的通 ...
- 已经为类型参数“Chart”指定了 constraint 子句。必须在单个 where 子句中指定类型参数的所有约束
public abstract class FillWorkBook<TModel, Chart> where TModel : struct where Chart : new() wh ...
- 爱上MVC~在Views的多级文件夹~续~分部页的支持
回到目录 之前写的一篇文章,主要针对View视图,它可以放在N级目录下,不必须非要在views/controller/action这种关系了,而在程序运行过程中,发现分页视图对本功能并不支持,原因很简 ...
- hibernate学习笔记之一 hibernate简介
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架 hibernate可以自动生成SQL语句,自 ...