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

接收到的消息和事件,其实都是微信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/tech/app/5303.html

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

相关推荐

发表回复

登录后才能评论