微信企业号开发:接收消息和事件

接收到的消息和事件,其实都是微信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;
        }
    }

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

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

效果如下:

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

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

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-05 11:11:33

微信企业号开发:接收消息和事件的相关文章

微信企业号开发—发送消息

开始回调模式后我们就要实现聊天功能了.平时使用微信聊天可以发送文本消息.语音.图片.视频等,这里只实现了其中的一些功能和大家分享. 一.与微信企业号建立连接 1.企业应用调用企业号提供的接口,管理或查询企业号后台所管理的资源.或给成员发送消息等,以下称主动调用模式. 2.企业号把用户发送的消息或用户触发的事件推送给企业应用,由企业应用处理,以下称回调模式. 3.用户在微信中阅读企业应用下发的H5页面,该页面可以调用微信提供的原生接口,使用微信开放的终端能力,以下称JSAPI模式. 这是微信企业号

微信企业号开发--手机删除键keyup事件无效

$('#input').keyup(function(){}); 其他的按键都是有效的,但是唯独删除按键无效. 以下方法可以解决: $('#input').bind('input propertychange', function () {});

微信企业号开发:消息类型与差别

微信企业号的消息.分成两种,调用微信接口发送的消息在接口文档中叫做发送消息,这样的消息是json格式的.在微信server给我们配置的URL post数据时发送的消息叫做接收消息和事件,须要加密解密,不须要调用微信接口,这样的消息的格式是XML格式的. 两种消息的差别和联系: 发送消息是json格式的,不须要加密解密.须要调用微信的接口,须要AccessToken,这样的消息适用于公众号主动推送下发消息. 接收消息和事件的格式是XML格式的,须要加密,不须要调用微信接口,不须要AccessTok

[转载]微信企业号开发如何建立连接

连接将使你的企业号更具价值,你可以使用以下三种方式,连接你的企业号及企业应用: 1.企业应用调用企业号提供的接口,管理或查询企业号后台所管理的资源.或给成员发送消息等,以下称主动调用模式. 2.企业号把用户发送的消息或用户触发的事件推送给企业应用,由企业应用处理,以下称回调模式. 3.用户在微信中阅读企业应用下发的H5页面,该页面可以调用微信提供的原生接口,使用微信开放的终端能力,以下称JSAPI模式. 通过这三种连接方式的结合,你可以在企业号中建立功能强大的移动轻应用,并依托微信数亿活跃用户,

微信企业号开发之回调模式的接口开发

一.前言 微信企业号应用中,有两种模式,一种是普通模式,这种模式只能进行简单网页链接,以及发送固定的消息.为了可以让企业号的用户更好的与应用交互,微信提供了回调模式,这种回调模式的可以将用户发送给微信的信息,转发到用户提供的一个回调接口上,该接口解析用户发送过来的信息,解析后进行相应,而且回调模式中,可以调用的东西不少,扫码,图片,视频,地理位置信息等. 在应用的模式下,选择回调模式,之后,需要设置3个参数(1.回调接口URL:2.token:3.ASESKey),URL就是提供的回调接口,微信

.net之微信企业号开发(三) 回调模式的接口开发

一.前言 微信企业号应用中,有两种模式,一种是普通模式,这种模式只能进行简单网页链接,以及发送固定的消息.为了可以让企业号的用户更好的与应用交互,微信提供了回调模式,这种回调模式的可以将用户发送给微信的信息,转发到用户提供的一个回调接口上,该接口解析用户发送过来的信息,解析后进行相应,而且回调模式中,可以调用的东西不少,扫码,图片,视频,地理位置信息等. 在应用的模式下,选择回调模式,之后,需要设置3个参数(1.回调接口URL:2.token:3.ASESKey),URL就是提供的回调接口,微信

微信企业号开发一:主动调用模式之TOKEN申请

微信企业号开发,数据访问格式分为以下三种: 1.企业应用调用企业号提供的接口,管理或查询企业号后台所管理的资源.或给成员发送消息等,以下称主动调用模式. 2.企业号把用户发送的消息或用户触发的事件推送给企业应用,由企业应用处理,以下称回调模式. 3.用户在微信中阅读企业应用下发的H5页面,该页面可以调用微信提供的原生接口,使用微信开放的终端能力,以下称JSAPI模式: 官方文档地址:http://qydev.weixin.qq.com/wiki/index.php 现在我们来看一下主动调用模式的

《微信企业号开发日志》本地调试程序四

这一节完成模拟微信回调发送消息!!! 先看效果图吧: 这个是模拟发送text文本消息的, 解析: 1.微信text文本最终是以2格式的xml文本post到我们的服务器接口的,1是对应xml各个字段值, 2.是最终需要post到我们服务器的xml明文 3.是将2中需要post到我们服务器的xml明文按照token,timestamp和nonce加密后的xml密文,(最终post到我们服务器的是这种经过加密后的xml,我们在接受接口中要先对其进行解密) 4.是将我们服务器中返回的xml明文展示出来

Force.com微信企业号开发系列(一) - 启用二次验证

微信于9月份推出企业号后引起了业界不小的反响,许多企业都在思索企业号将如何影响企业的运营,从本文开始,我将详细阐述微信企业号开发的相关知识,而本文将着重介绍如何实现更高安全机制的二次验证. 申请企业体验号: 企业号顾名思义就是企业来申请的号,申请时就像申请服务号一样,需要提供各种组织证明文件,对广大开发者来说很难操作,好在腾讯公司也像服务号一样开通了体验号申请,留意企业体验号的有效期间非常短,只有90天(服务号测试账号有1年有效期),且如果企业体验号长期不使用还会收到腾讯公司的提前失效提醒邮件.