关键词:
接收到的消息和事件,其实都是微信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;
开启应用的回调模式调用使用的代码参考 微信企业号开发:启用回调模式
这样修改之后呢,用户给企业号发送文本消息时,企业号就可以把用户发送的消息主动回复给用户。
效果如下:
其他的类型的普通消息,也都相似。
但我个人发现,收到事件时,发送被动响应消息,似乎不保证用户能收到,似乎有很大的概率收不到,不知道是我人的原因,还是微信的原因。但奇怪的是,事件都能收到,发送被动响应消息,很大的概率收不到。
其实收到普通的消息时,也可以通过主动发送消息,也就是调用微信的相关接口,也可以达到回复用户的目的,这个我测试过,但比发送被动响应消息慢,也能实现和上边类似的效果。
微信公众号开发之入门篇(代码片段)
本篇技术博客来自Worktile微信之父@龚林杰的动情分享,为您详细讲述我们微信公众号【getworktile】背后的点点滴滴~现如今,微信已经不再只承担着交流沟通、娱乐大众的功能,微信公众号的推出将微信逐渐转变成... 查看详情
java开发微信公众号---微信服务器请求消息,响应消息,事件消息以及工具处理类的封装(代码片段)
在前面几篇文章我们讲了微信公众号环境的配置和微信公众号服务的接入,接下来我们来说一下微信服务器请求消息,响应消息以及事件消息的相关内容,首先我们来分析一下消息类型和返回xml格式及实体类的封装。(一)封装... 查看详情
微信开发小结——积累与沉淀(代码片段)
前言微信开发是个人、企业或组织在拥有超大用户群体的微信应用上,利用微信公众平台,开发类似插件或服务的轻应用。微信公众平台分为三种:订阅号:主要面向媒体和个人,旨在为用户提供信息资讯ÿ... 查看详情
前端工程实训(代码片段)
...础课的内容,复习时可以参考发的小程序ppt小程序概念、微信开发概念小程序账号申请、工具下载使用学习一下小程序项目结构、小程序生命周期概念小程序中写页面的模板语法小程序中写交互的事件的相关知识小程序页面跳转... 查看详情
.net微信公众号开发——模板消息
本文介绍微信公众号中的模板消息,包括以下内容:(1)TemplateMessage类简介;(2)设置所属行业;(3)获得模板id;(4)发送模板消息;(5)接收推送模板消息发送结果事件。 本文演示地址:http://xrwang.... 查看详情
.net微信公众号开发——群发消息
本文将介绍微信公众号开发中用于群发消息的类MassMessage,包括:(1)MassMessage类;(2)群发;(3)删除;(4)预览;(5)查询发送状态;(6)接收推送群发结果事件。 源代码地址:http://git.oschina.net/xrwang2/x... 查看详情
微信的新消息推送是怎么实现的(企业号开发)
具体操作方法是,企业申请账号后,员工通过自己的微信订阅该企业号,然后可以使用目录下的各种功能模块,完成企业内部管理和交流。用户可以通过微信完成打卡、报销、会议等事务。用微信编辑器先编写好内容。和原来的... 查看详情
验证接入(代码片段)
公众号的分类:我们平常在微信应用上会看到有很多的公众号,但是各自并不一样,公众号也分很多种类型,不过最常见的就是服务号和订阅号了。下面我们来看一下他们的区别:1、订阅号为媒体和个人提供一种信息传播方式... 查看详情
java调用企业微信接口给微信发消息(代码片段)
这里的消息是企业微信和微信都能收到消息1.注册一个企业微信,记住企业ID(我的企业-企业id)2.应用管理--创建应用(记住AgentId和Secret)3.下面是代码packagecom.jingmai.video.live.messagecenter.utils;/***@ClassNameWeChatData*@Description*@Au... 查看详情
微信公众号发送模板消息(代码片段)
目录1开发中遇到的问题汇总2模板消息创建3调试接口3.1微信公众号消息模板3.1.1基本信息3.2请求参数3.2.1Query参数及说明3.2.2body参数及说明4测试结果5工具类封装1开发中遇到的问题汇总首先是在测试中,遇到最多的就是41003->appid... 查看详情
给微信公众号发消息,他能收到吗?
...大部分不会回复,但会收到参考技术A会的公众号是由java微信开发-消息接收和自动回复,因此公众号是会恢复信息的,但是恢复信息的内容不一定是你想要得到的答案。1、信公众号开发现在在企业应用中,比较火爆的一款应用... 查看详情
unigui接收普通消息和被动回复用户消息
...格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况... 查看详情
微信公众号及小程序开发入门(代码片段)
开发过程中一些对微信公众号和小程序的认识。一、服务号在公众号开发入门一中提到,微信公众号分为订阅号和服务号,其中服务号功能最强,只准企业申请,并且要每年交300元认证费。如果企业想拥有自己的... 查看详情
python借助企业微信群机器人推送消息和文件(代码片段)
企业微信功能日益强大,腾讯官方也提供了丰富的API供开发者实现企业微信的很多自动化场景。比如,如何给某个企业微信群发送消息、图片或者文件,甚至@指定群用户?别急,看小爬君轻松教会大家。Step1:在企业微信PC... 查看详情
腾讯系:微信,公众号,小程序,企业微信开发知识概括(代码片段)
腾讯系:公众号,小程序,企业微信等等开发知识概括企业微信公众号小程序微信开放平台总结企业微信第三方应用开发(sass服务商):概述:第三方应用接口旨在方便企业微信管理员通过简单的操作来使用第三方服... 查看详情
微信公众号好开发--开发模式(代码片段)
一、消息回复接口1.1微信公众平台测试号个人订阅号有很多接口没有权限,可以申请一个测试账号申请测试账号扫码后配置URL和token,并扫码关注测试账号,之后获取所有权限1.2、其他消息回复1.2.1图文消息图文消息... 查看详情
unigui接收普通消息和被动回复用户消息
...格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此 查看详情
unigui接收普通消息和被动回复用户消息
...格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此 查看详情