微信企业号开发:接收消息和事件详解手机开发

接收到的消息和事件,其实都是微信post到我们配置的URL的消息。接收普通消息就是用户给公众号发送的消息,事件是由于用户的特定操作,微信post给我们的消息。被动响应消息是我们收到微信post过来的普通消息或者是事件时,企业号通过Response.Write这种方式回复的消息。


核心代码:

把微信post过来的数据先解密,转为能处理的XML,再把XML转为对象

 #region 将POST过来的数据转化成实体对象 
/// <summary> 
/// 将微信POST过来的数据转化成实体对象 
/// </summary> 
/// <param name="token"></param> 
/// <returns></returns> 
public static ReceiveMessageBase ConvertMsgToObject(string msgBody = "") 
{           
if (string.IsNullOrWhiteSpace(msgBody)) 
{ 
Stream s = System.Web.HttpContext.Current.Request.InputStream; 
byte[] b = new byte[s.Length]; 
s.Read(b, 0, (int)s.Length); 
msgBody = Encoding.UTF8.GetString(b); 
} 
string CorpToken = AppIdInfo.GetToken(); 
string corpId = AppIdInfo.GetCorpId(); 
string encodingAESKey = AppIdInfo.GetEncodingAESKey(); 
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(CorpToken, encodingAESKey, corpId); 
string msg_signature = HttpContext.Current.Request.QueryString["msg_signature"]; 
string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; 
string nonce = HttpContext.Current.Request.QueryString["nonce"]; 
string sMsg = "";  // 解析之后的明文 
int flag = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, msgBody, ref sMsg); 
if (flag == 0) 
{ 
msgBody = sMsg; 
LogInfo.Info("解密后的消息为" + sMsg); 
} 
else 
{ 
LogInfo.Error("解密消息失败!flag=" + flag); 
}            
if (string.IsNullOrWhiteSpace(msgBody)) 
{ 
return null; 
} 
XmlDocument doc = null; 
MsgType msgType = MsgType.UnKnown; 
EventType eventType = EventType.UnKnown; 
ReceiveMessageBase msg = new ReceiveMessageBase(); 
msg.MsgType = msgType; 
msg.MessageBody = msgBody; 
XmlNode node = null; 
XmlNode tmpNode = null; 
try 
{ 
doc = new XmlDocument(); 
doc.LoadXml(msgBody);//解密后才是需要处理的XML数据,读取XML字符串 
XmlElement rootElement = doc.DocumentElement; 
XmlNode msgTypeNode = rootElement.SelectSingleNode("MsgType");//获取字符串中的消息类型 
node = rootElement.SelectSingleNode("FromUserName"); 
if (node != null) 
{ 
msg.FromUserName = node.InnerText; 
} 
node = rootElement.SelectSingleNode("AgentID"); 
if (node != null) 
{ 
msg.AgentID = Convert.ToInt32(node.InnerText); 
} 
node = rootElement.SelectSingleNode("ToUserName"); 
if (node != null) 
{ 
msg.ToUserName = node.InnerText; 
} 
node = rootElement.SelectSingleNode("CreateTime"); 
if (node != null) 
{ 
msg.CreateTime = Convert.ToInt64(node.InnerText); 
} 
#region 获取具体的消息对象 
string strMsgType = msgTypeNode.InnerText; 
string msgId = string.Empty; 
string content = string.Empty; 
tmpNode = rootElement.SelectSingleNode("MsgId"); 
if (tmpNode != null) 
{ 
msgId = tmpNode.InnerText.Trim(); 
} 
string strMsgType2 = strMsgType.Trim().ToLower(); 
switch (strMsgType2) 
{ 
case "text"://接收普通消息 text消息 
msgType = MsgType.Text; 
tmpNode = rootElement.SelectSingleNode("Content"); 
if (tmpNode != null) 
{ 
content = tmpNode.InnerText.Trim(); 
} 
TextReceiveMessage txtMsg = new TextReceiveMessage(msg) 
{                            
MsgType = msgType,                         
MsgId = Convert.ToInt64(msgId), 
Content = content 
}; 
txtMsg.AfterRead(); 
return txtMsg; 
case "image"://接收普通消息 image消息 
msgType = MsgType.Image; 
ImageReceiveMessage imgMsg = new ImageReceiveMessage(msg) 
{                            
MsgId = Convert.ToInt64(msgId), 
MsgType = msgType,                           
MediaId = rootElement.SelectSingleNode("MediaId").InnerText, 
PicUrl = rootElement.SelectSingleNode("PicUrl").InnerText 
}; 
imgMsg.AfterRead(); 
return imgMsg; 
case "voice"://接收普通消息 voice消息 
msgType = MsgType.Voice; 
XmlNode node1 = rootElement.SelectSingleNode("Recognition"); 
if (node1 != null) 
{ 
msgType = MsgType.VoiceResult; 
} 
VoiceReceiveMessage voiceMsg = new VoiceReceiveMessage(msg) 
{                            
MsgId = Convert.ToInt64(msgId), 
MsgType = msgType, 
Recognition = node1 == null ? string.Empty : node1.InnerText.Trim(), 
Format = rootElement.SelectSingleNode("Format").InnerText, 
MediaId = rootElement.SelectSingleNode("MediaId").InnerText 
}; 
voiceMsg.AfterRead(); 
return voiceMsg; 
case "video"://接收普通消息 video消息 
msgType = MsgType.Video; 
VideoReceiveMessage videoMsg = new VideoReceiveMessage(msg) 
{                           
MediaId = rootElement.SelectSingleNode("MediaId").InnerText,                           
MsgId = Convert.ToInt64(msgId), 
MsgType = msgType,                           
ThumbMediaId = rootElement.SelectSingleNode("ThumbMediaId").InnerText 
}; 
videoMsg.AfterRead(); 
return videoMsg; 
case "location"://接收普通消息 location消息 
msgType = MsgType.Location; 
LocationReceiveMessage locationMsg = new LocationReceiveMessage(msg) 
{                          
MsgId = Convert.ToInt64(msgId), 
MsgType = msgType,                        
Label = rootElement.SelectSingleNode("Label").InnerText, 
Location_X = rootElement.SelectSingleNode("Location_X").InnerText, 
Location_Y = rootElement.SelectSingleNode("Location_Y ").InnerText, 
Scale = rootElement.SelectSingleNode("Scale").InnerText 
}; 
locationMsg.AfterRead(); 
return locationMsg; 
case "event":// 接收事件 
msgType = MsgType.Event; 
eventType = EventType.UnKnown; 
msg.MsgType = msgType; 
XmlNode eventNode = rootElement.SelectSingleNode("Event"); 
if (eventNode != null) 
{ 
string eventtype = eventNode.InnerText.Trim().ToLower(); 
switch (eventtype) 
{ 
case "subscribe": //接收事件 成员关注 
eventType = EventType.Subscribe; 
SubscribeEventMessage subEvt = new SubscribeEventMessage(msg) 
{ 
EventType = EventType.Subscribe,                                       
MsgType = msgType,                                       
}; 
subEvt.AfterRead(); 
return subEvt; 
case "unsubscribe": //接收事件 取消关注事件 
eventType = EventType.UnSubscribe; 
UnSubscribeEventMessage unSubEvt = new UnSubscribeEventMessage(msg) 
{ 
EventType = eventType,                                    
MsgType = msgType,                                      
}; 
unSubEvt.AfterRead(); 
return unSubEvt; 
case "location"://接收事件 上报地理位置事件 
eventType = EventType.Location; 
UploadLocationEventMessage locationEvt = new UploadLocationEventMessage(msg) 
{                                        
EventType = eventType,                                       
Latitude = rootElement.SelectSingleNode("Latitude").InnerText, 
Longitude = rootElement.SelectSingleNode("Longitude").InnerText,                                      
MsgType = msgType, 
Precision = rootElement.SelectSingleNode("Precision").InnerText, 
}; 
locationEvt.AfterRead(); 
return locationEvt; 
case "click": //接收事件 上报菜单事件 点击菜单拉取消息的事件推送 
eventType = EventType.Click; 
MenuEventMessage menuEvt = new MenuEventMessage(msg) 
{ 
EventKey = rootElement.SelectSingleNode("EventKey").InnerText, 
EventType = eventType,                                     
MsgType = msgType, 
}; 
menuEvt.AfterRead(); 
return menuEvt; 
case "view": //接收事件 上报菜单事件 点击菜单跳转链接的事件推送 
eventType = EventType.VIEW; 
MenuEventVIEWEventMessage menuVIEWEvt = new MenuEventVIEWEventMessage(msg) 
{ 
EventKey = rootElement.SelectSingleNode("EventKey").InnerText, 
EventType = eventType,                                      
MsgType = msgType,                                    
}; 
menuVIEWEvt.AfterRead(); 
return menuVIEWEvt; 
case "scancode_push"://接收事件 上报菜单事件 扫码推事件的事件推送 
eventType = EventType.scancode_push; 
ScanCodePushEventMessage scanCodePushEventMessage = new ScanCodePushEventMessage(msg) 
{                                      
EventKey = rootElement.SelectSingleNode("EventKey").InnerText, 
EventType = eventType,                                      
MsgType = msgType,                                     
ScanCodeInfo = new ScanCodeInfo(rootElement.SelectSingleNode("ScanCodeInfo")) 
}; 
scanCodePushEventMessage.AfterRead(); 
return scanCodePushEventMessage; 
case "scancode_waitmsg"://接收事件 上报菜单事件 扫码推事件且弹出“消息接收中”提示框的事件推送 
eventType = EventType.scancode_waitmsg; 
ScanCodeWaitMsgEventMessage scanCodeWaitMsgEventMessage = new ScanCodeWaitMsgEventMessage(msg) 
{                                       
EventKey = rootElement.SelectSingleNode("EventKey").InnerText, 
EventType = eventType,                                       
MsgType = msgType,                                       
ScanCodeInfo = new ScanCodeInfo(rootElement.SelectSingleNode("ScanCodeInfo")) 
}; 
scanCodeWaitMsgEventMessage.AfterRead(); 
return scanCodeWaitMsgEventMessage; 
case "pic_sysphoto"://接收事件 上报菜单事件 弹出系统拍照发图的事件推送 
eventType = EventType.pic_sysphoto; 
PicSysPhotoEventMessage picSysPhotoEventMessage = new PicSysPhotoEventMessage(msg) 
{                                     
EventKey = rootElement.SelectSingleNode("EventKey").InnerText,                                       
MsgType = msgType,                                       
SendPicsInfo = new SendPicsInfo(rootElement.SelectSingleNode("SendPicsInfo")) 
}; 
picSysPhotoEventMessage.AfterRead(); 
return picSysPhotoEventMessage; 
case "pic_photo_or_album"://接收事件 上报菜单事件 弹出拍照或者相册发图的事件推送 
eventType = EventType.pic_photo_or_album; 
PicPhotoOrAlbumEventMessage picPhotoOrAlbumEventMessage = new PicPhotoOrAlbumEventMessage(msg) 
{                                      
EventType = eventType, 
EventKey = rootElement.SelectSingleNode("EventKey").InnerText,                                   
MsgType = msgType,                                       
SendPicsInfo = new SendPicsInfo(rootElement.SelectSingleNode("SendPicsInfo")) 
}; 
picPhotoOrAlbumEventMessage.AfterRead(); 
return picPhotoOrAlbumEventMessage; 
case "pic_weixin"://接收事件 上报菜单事件 弹出微信相册发图器的事件推送 
eventType = EventType.pic_weixin; 
PicWeiXinEventMessage picWeiXinEventMessage = new PicWeiXinEventMessage(msg) 
{                                     
EventType = eventType, 
EventKey = rootElement.SelectSingleNode("EventKey").InnerText,                                       
MsgType = msgType,                                       
SendPicsInfo = new SendPicsInfo(rootElement.SelectSingleNode("SendPicsInfo")) 
}; 
picWeiXinEventMessage.AfterRead(); 
return picWeiXinEventMessage; 
case "location_select"://接收事件 上报菜单事件 弹出地理位置选择器的事件推送 
eventType = EventType.location_select; 
LocationSelectEventMessage locationSelectEventMessage = new LocationSelectEventMessage(msg) 
{                                     
EventType = eventType, 
EventKey = rootElement.SelectSingleNode("EventKey").InnerText,                                      
MsgType = msgType,                                       
SendLocationInfo = new SendLocationInfo(rootElement.SelectSingleNode("SendLocationInfo"))                                       
}; 
locationSelectEventMessage.AfterRead(); 
return locationSelectEventMessage; 
case "enter_agent": //接收事件 成员进入应用的事件推送 
eventType = EventType.enter_agent; 
EnterAgentEventMessage EnterAgentEventMessage = new EnterAgentEventMessage(msg) 
{   
MsgType = msgType,                                     
}; 
EnterAgentEventMessage.AfterRead(); 
return EnterAgentEventMessage; 
default: 
LogInfo.Error("事件类型" + eventtype + "未处理"); 
break; 
} 
} 
break; 
default: 
LogInfo.Error("消息类型" + strMsgType2 + "未处理"); 
break; 
} 
msg.MsgType = msgType; 
#endregion 
} 
catch (Exception ex) 
{ 
LogInfo.Error("处理消息异常:" + msgBody, ex); 
} 
finally 
{ 
if (doc != null) 
{ 
doc = null; 
} 
} 
msg.MsgType = msgType; 
return msg; 
}

发送被动响应文本消息:

  /// <summary> 
/// 发送被动响应文本消息,需要先加密在发送 
/// </summary> 
/// <param name="fromUserName">发送方</param> 
/// <param name="toUserName">接收方</param> 
/// <param name="content">文本内容</param> 
public static void SendTextReplyMessage(string fromUserName, string toUserName, string content) 
{ 
TextReplyMessage msg = new TextReplyMessage() 
{ 
CreateTime = Tools.ConvertDateTimeInt(DateTime.Now), 
FromUserName = fromUserName, 
ToUserName = toUserName, 
Content = content 
}; 
/*  LogInfo.Info("发送信息2sMsg=" + content);//也可以使用微信的接口发送消息 
TextMsg data = new TextMsg(content); 
data.agentid = "7"; 
data.safe = "0"; 
// data.toparty = "@all"; 
//   data.totag = "@all"; 
data.touser = toUserName; 
BLLMsg.SendMessage(data);*/ 
string CorpToken = AppIdInfo.GetToken(); 
string corpId = AppIdInfo.GetCorpId(); 
string encodingAESKey = AppIdInfo.GetEncodingAESKey(); 
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(CorpToken, encodingAESKey, corpId); 
string msg_signature = HttpContext.Current.Request.QueryString["msg_signature"]; 
string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; 
string nonce = HttpContext.Current.Request.QueryString["nonce"]; 
string encryptResponse = "";//加密后的文字 
string sMsg = msg.ToXmlString();//加密前的文字 
int isok = wxcpt.EncryptMsg(sMsg, timestamp, nonce, ref encryptResponse);// 
LogInfo.Info("发送信息sMsg=" + sMsg); 
//   LogInfo.Info("发送信息encryptResponse=" + encryptResponse); 
if (isok == 0 && !string.IsNullOrEmpty(encryptResponse)) 
{ 
HttpContext.Current.Response.ContentEncoding = Encoding.UTF8; 
HttpContext.Current.Response.Write(encryptResponse);//被动相应消息不需要调用微信接口 
} 
else { 
LogInfo.Info("发送信息失败isok=" + isok); 
}    
}

注释掉的代码就是主动发送消息,具体可参考微信企业号开发:主动发送消息

使用注释掉的代码也可以给用户发送消息,但这种方式不叫被动响应消息


被动响应消息实体

 /// <summary> 
/// 被动响应消息类 
/// </summary> 
public abstract class ReplyMessage 
{ 
public string ToUserName { get; set; } 
public string FromUserName { get; set; } 
public long CreateTime { get; set; } 
/// <summary> 
/// 将对象转化为Xml消息 
/// </summary> 
/// <returns></returns> 
public abstract  string ToXmlString(); 
} 
/// <summary> 
/// 被动响应文本消息 
/// </summary> 
public class TextReplyMessage : ReplyMessage 
{ 
/// <summary> 
/// 回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示)  
/// </summary> 
public string Content { get; set; } 
/// <summary> 
/// 将对象转化为Xml消息 
/// </summary> 
/// <returns></returns> 
public override string ToXmlString() 
{ 
string s = "<xml><ToUserName><![CDATA[{0}]]></ToUserName><FromUserName><![CDATA[{1}]]></FromUserName><CreateTime>{2}</CreateTime><MsgType><![CDATA[{3}]]></MsgType><Content><![CDATA[{4}]]></Content></xml>"; 
s = string.Format(s, 
ToUserName ?? string.Empty, 
FromUserName ?? string.Empty, 
CreateTime.ToString(), 
"text", 
Content ?? string.Empty 
); 
return s; 
} 
}

配置的URL网页的代码:

public class TestWeixin : IHttpHandler { 
public void ProcessRequest (HttpContext context) {       
if (context.Request.HttpMethod.ToLower() == "post") 
{ 
try 
{ 
System.IO.Stream s = context.Request.InputStream; 
byte[] b = new byte[s.Length]; 
s.Read(b, 0, (int)s.Length); 
string postStr = System.Text.Encoding.UTF8.GetString(b); 
if (!string.IsNullOrEmpty(postStr)) 
{ 
Execute(postStr); 
} 
}catch(Exception e) 
{ 
new AppException("收到信息异常" + e.Message); 
}     
} 
else //开启应用的回调模式调用 ,代码省略 
{             
} 
} 
private void Execute(string postStr) 
{ 
ReceiveMessageBase basemsg = ConvertMsgToObject(postStr); 
if (basemsg.MsgType ==.MsgType.Text) 
{ 
TextReceiveMessage txtMsg = basemsg as TextReceiveMessage; 
if (txtMsg != null) 
{ 
SendTextReplyMessage(txtMsg.ToUserName, txtMsg.FromUserName, "收到文本消息:" + txtMsg.Content);//发送被动消息 
} 
} 
} 
public bool IsReusable { 
get { 
return false; 
} 
}

开启应用的回调模式调用使用的代码参考
微信企业号开发:启用回调模式

这样修改之后呢,用户给企业号发送文本消息时,企业号就可以把用户发送的消息主动回复给用户。

效果如下:

微信企业号开发:接收消息和事件详解手机开发

其他的类型的普通消息,也都相似。

但我个人发现,收到事件时,发送被动响应消息,似乎不保证用户能收到,似乎有很大的概率收不到,不知道是我人的原因,还是微信的原因。但奇怪的是,事件都能收到,发送被动响应消息,很大的概率收不到。

其实收到普通的消息时,也可以通过主动发送消息,也就是调用微信的相关接口,也可以达到回复用户的目的,这个我测试过,但比发送被动响应消息慢,也能实现和上边类似的效果。

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/5303.html

(0)
上一篇 2021年7月16日
下一篇 2021年7月16日

相关推荐

发表回复

登录后才能评论