经过上一篇的介绍,我们应该大致对微信平台下 的公众号开发有个了解,其实也就那么一回事,现在我们首先来说说微信开发的"消息",然后再点一下微信中的"关注".
微信公众平台的消息是以XML数据格式进行封装和传递的,那么简单来说,消息分为两种咯,就是用户发送的消息以及公众号回复的消息.
以最简单的文本消息为列,消息格式如下:
<xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime>
<MsgType><![CDATA[{1}]]></MsgType>
<Content><![CDATA[{4}]]></Content>
<MsgId>{5}</MsgId>
</xml>
/// <summary> /// 发送方帐号 /// </summary> public string FromUserName { get; set; } /// <summary> /// 接收方账号 /// </summary> public string ToUserName { get; set; } /// <summary> /// 消息类型 /// </summary> public string MsgType { get; protected set; } /// <summary> /// 创建时间 /// </summary> public string CreateTime { get; set; } /// <summary> /// 内容 /// </summary> public string Content { get; set; } /// <summary> /// 消息ID /// </summary> public string MsgId { get; set; }
用户发送消息数据实体:
文本消息,图片消息,语言消息,视频消息,地理位置消息,链接消息,事件消息.
其中,事件消息:关注/取消关注事件,扫描二维码事件,上报地理位置事件,自定义菜单事件.
公众号回复得消息数据实体也是相对应的.
在实际开发中,我们程序首先得到的是微信服务器转发过来的请求,我们需要对Request进行解析然后得到XML字符串,代码如下:
/// <summary> /// 读取请求对象的内容 /// 只能读一次 /// </summary> /// <param name="request">HttpRequest对象</param> /// <returns>XML</returns> public static string ReadRequest(HttpRequest request) { string reqStr = string.Empty; using (Stream s = request.InputStream) { using (StreamReader reader = new StreamReader(s, Encoding.UTF8)) { reqStr = reader.ReadToEnd(); } } return reqStr; }
我们需要将XML数据报转化为对应的C#数据实体,以及将实体封装为XML数据报回复,.net framewokr提供了一套xml数据的完整操作.在system.xml命名空间下,以文本消息为例,如下:
/// <summary> /// 从xml数据加载文本消息 /// </summary> /// <param name="xml"></param> public static TextMessage LoadFromXml(string xml) { TextMessage tm = null; if (!string.IsNullOrEmpty(xml)) { XElement element = XElement.Parse(xml); if (element != null) { tm = new TextMessage(); tm.FromUserName = element.Element(FROM_USERNAME).Value; tm.ToUserName = element.Element(TO_USERNAME).Value; tm.CreateTime = element.Element(CREATE_TIME).Value; tm.Content = element.Element(CONTENT).Value; tm.MsgId = element.Element(MSG_ID).Value; } } return tm; }
接下来我们就可以知道用户发送了一个什么样的消息类型给服务器(MsgType),这样,我们需要定义不同消息类型的处理类,比如TextHandler:
/// <summary> /// 处理请求 /// </summary> /// <returns></returns> public string HandleRequest() { string response = string.Empty; TextMessage tm = TextMessage.LoadFromXml(RequestXml); string content = tm.Content.Trim(); if (string.IsNullOrEmpty(content)) { response = "消息不能为空"; } if(content.Contains("你好")) { response = "hello,I am YZR" } tm.Content = response; //进行发送者、接收者转换 string temp = tm.ToUserName; tm.ToUserName = tm.FromUserName; tm.FromUserName = temp; response = tm.GenerateContent(); return response; }
GenerateContent()返回一定格式的XML字符串:
/// <summary> /// 生成内容 /// </summary> /// <returns></returns> public override string GenerateContent() { this.CreateTime = Common.GetNowTime(); return string.Format(this.Template,this.ToUserName,this.FromUserName,this.CreateTime,this.MsgType,this.Content,this.MsgId);//模板template } /// <summary> /// 加载模板 /// </summary> private static void LoadTemplate() { m_Template = @"<xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime> <MsgType><![CDATA[{3}]]></MsgType> <Content><![CDATA[{4}]]></Content> <MsgId>{5}</MsgId> </xml>"; }
最后将XML返回回去完成回复操作:
//由微信服务接收请求,具体处理请求 WeiXinService wxService = new WeiXinService(context.Request); string responseMsg = wxService.Response(); context.Response.Clear(); context.Response.Charset = "UTF-8"; context.Response.Write(responseMsg); context.Response.End();
那么现在对于微信的"关注"事件,应该也是一件很容易的事情了.首先通过以get方式URL接入,之后用户发起"关注"事件消息传送,我们需要先得到Request中的XML字符串数据(解析Request得到XML之后进行判断消息的MsgType),再对关注事件作出服务器的响应回去客户端,将数据封装成XML在Response中输出即可.
/// <summary> /// 处理请求 /// </summary> /// <param name="context"></param> public void ProcessRequest(HttpContext context) { //由微信服务接收请求,具体处理请求 WeiXinService wxService = new WeiXinService(context.Request); string responseMsg = wxService.Response(); context.Response.Clear(); context.Response.Charset = "UTF-8"; context.Response.Write(responseMsg); context.Response.End(); }
/// <summary> /// 处理请求,产生响应 /// </summary> /// <returns></returns> public string Response() { string method = Request.HttpMethod.ToUpper(); //验证签名 if (method == "GET") { if (CheckSignature()) { return Request.QueryString[ECHOSTR];//随机字符串 } else { return "error"; } } //处理消息 if (method == "POST") { return ResponseMsg(); } else { return "无法处理"; } }
/// <summary> /// 处理请求 /// </summary> /// <returns></returns> private string ResponseMsg() { string requestXml = Common.ReadRequest(this.Request);//得到Request的XML字符串信息 IHandler handler = HandlerFactory.CreateHandler(requestXml);//处理器 if (handler != null) { return handler.HandleRequest();//处理器处理请求 } return string.Empty; }
/// <summary> /// 创建处理器 /// </summary> /// <param name="requestXml">请求的xml</param> /// <returns>IHandler对象</returns> public static IHandler CreateHandler(string requestXml) { IHandler handler = null; if (!string.IsNullOrEmpty(requestXml)) { //解析数据 XmlDocument doc = new System.Xml.XmlDocument(); doc.LoadXml(requestXml); XmlNode node = doc.SelectSingleNode("/xml/MsgType"); if (node != null) { XmlCDataSection section = node.FirstChild as XmlCDataSection; if (section != null) { string msgType = section.Value; switch (msgType) { case "text": handler = new TextHandler(requestXml); //文本处理 break; case "event": handler = new EventHandler(requestXml); //事件处理 break; } } } } return handler; }
/// <summary> /// 处理请求 /// </summary> /// <returns></returns> public string HandleRequest() { string response = string.Empty; EventMessage em = EventMessage.LoadFromXml(RequestXml); if (em.Event.Equals("subscribe",StringComparison.OrdinalIgnoreCase)) { //回复欢迎消息 TextMessage tm = new TextMessage(); tm.ToUserName = em.FromUserName; tm.FromUserName = em.ToUserName; tm.CreateTime = Common.GetNowTime(); tm.Content = "欢迎您关注"; response = tm.GenerateContent(); } return response; }
最后补充一下EventMessage:
class EventMessage : Message { private const string EVENT = "Event"; private const string EVENT_KEY = "EventKey"; /// <summary> /// 模板 /// </summary> private static string mTemplate; /// <summary> /// 模板 /// </summary> public override string Template { get { if (string.IsNullOrEmpty(mTemplate)) { mTemplate = @"<xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[{3}]]></Event> <EventKey>{4}</EventKey> </xml>"; } return mTemplate; } } /// <summary> /// 事件类型 /// </summary> public string Event { get; set; } /// <summary> /// 事件KEY值,与自定义菜单接口中KEY值对应 /// </summary> public string EventKey { get; set; } /// <summary> /// 构造函数 /// </summary> public EventMessage() { this.MsgType = "event"; } /// <summary> /// 从xml数据加载文本消息 /// </summary> /// <param name="xml"></param> public static EventMessage LoadFromXml(string xml) { EventMessage em = null; if (!string.IsNullOrEmpty(xml)) { XElement element = XElement.Parse(xml); if (element != null) { em = new EventMessage(); em.FromUserName = element.Element(Common.FROM_USERNAME).Value; em.ToUserName = element.Element(Common.TO_USERNAME).Value; em.CreateTime = element.Element(Common.CREATE_TIME).Value; em.Event =element.Element(EVENT).Value; em.EventKey = element.Element(EVENT_KEY).Value; } } return em; } }
下一次,我们来看一看,自定义菜单的实现.