zoukankan      html  css  js  c++  java
  • 《微信企业号开发日志》本地调试程序二

    上一节完成了微信回调测试,功能呢其实也没什么价值,只是个人使用。。

    这一节,我们设置微信响应消息调试:

    先来看看微信企业号官方的说明:来自 http://qydev.weixin.qq.com/wiki/index.php?title=%E5%9B%9E%E8%B0%83%E6%A8%A1%E5%BC%8F

    -----------------------------------------------------------------------------------------------------------------------------------------------------------

    企业号在回调企业URL时,会对消息体本身做AES加密,以XML格式POST到企业应用的URL上;企业在被动响应时,也需要对数据加密,以XML格式返回给微信。企业的回复支持文本、图片、语音、视频、图文等格式

    微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。如果在调试中,发现员工无法收到响应的消息,可以检查是否消息处理超时。

    关于重试的消息排重,有msgid的消息推荐使用msgid排重。事件类型消息推荐使用FromUserName + CreateTime排重。

    假如企业无法保证在五秒内处理并回复,可以直接回复空串,企业号不会对此作任何处理,并且不会发起重试。这种情况下,可以使用发消息接口进行异步回复。

    假设企业回调URL为http://api.3dept.com

    • 请求说明:

    http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS&timestamp=13500001234&nonce=123412323

    • 回调数据格式:
    <xml> 
       <ToUserName><![CDATA[toUser]]</ToUserName>
       <AgentID><![CDATA[toAgentID]]</AgentID>
       <Encrypt><![CDATA[msg_encrypt]]</Encrypt>
    </xml>
    
    1.msg_encrypt为经过加密的密文
    2.AgentID为接收的应用id,可在应用的设置页面获取
    3.ToUserName为企业号的CorpID
    

    企业需要对msg_signature进行校验,并解密msg_encrypt,得出msg的原文。

    • 被动响应给微信的数据格式:
    <xml>
       <Encrypt><![CDATA[msg_encrypt]]></Encrypt>
       <MsgSignature><![CDATA[msg_signature]]></MsgSignature>
       <TimeStamp>timestamp</TimeStamp>
       <Nonce><![CDATA[nonce]]></Nonce>
    </xml>
    
    1.msg_encrypt为经过加密的密文,算法参见附录
    2.MsgSignature为签名,算法参见附录
    3.TimeStamp为时间戳,Nonce为随机数,由企业自行生成
    


    接收消息时的加解密处理

    企业可以直接使用微信提供的库进行加解密的处理,目前提供的有c++/python/php/java/c#等语言版本。代码提供了解密、加密、验 证URL三个接口,企业可根据自身需要下载(参见附录)。以下为库函数的使用说明(以c++为例),更详细的加解密方案请参考附录。

    1、解密函数

    int DecryptMsg(const string &sMsgSignature, const string &sTimeStamp, const string &sNonce, const string &sPostData, string &sMsg);
    
    • 参数说明
    参数必须说明
    sMsgSignature 从回调URL中获取的msg_signature参数
    sTimeStamp 从回调URL中获取的timestamp参数
    sNonce 从回调URL中获取的nonce参数
    sPostData 从回调URL中获取的整个post数据
    sMsg 用于返回解密后的msg,以xml组织
    • 返回说明

    请参阅附录加解密部分。

    2、加密函数

    int EncryptMsg(const string &sReplyMsg, const string &sTimeStamp, const string &sNonce, string &sEncryptMsg);
    
    • 参数说明
    参数必须说明
    sReplyMsg 返回的消息体原文
    sTimeStamp 时间戳,调用方生成
    sNonce 随机数,调用方生成
    sEncryptMsg 用于返回的密文,以xml组织

    -----------------------------------------------------------------------------------------------------------------------------------------------------------

    试图模拟微信加密

    • 被动响应给微信的数据格式:
    <xml>
       <Encrypt><![CDATA[msg_encrypt]]></Encrypt>
       <MsgSignature><![CDATA[msg_signature]]></MsgSignature>
       <TimeStamp>timestamp</TimeStamp>
       <Nonce><![CDATA[nonce]]></Nonce>
    </xml>
    
    1.msg_encrypt为经过加密的密文,算法参见附录
    2.MsgSignature为签名,算法参见附录
    3.TimeStamp为时间戳,Nonce为随机数,由企业自行生成
    
    试图模拟msg_encrypt加密算法,官方这样说:

    为了验证调用者的合法性,微信在回调url中增加了消息签名,以参数msg_signature标识,企业需要验证此参数的正确性后再解密。验证步骤:

    1.企业计算签名:dev_msg_signature=sha1(sort(token、timestamp、nonce、msg_encrypt))。sort的含义是将参数按照字母字典排序,然后从小到大拼接成一个字符串

    自己觉得搞不定,希望谁有这算法请告之,小弟谢过

    无奈,只能跳过这个加密,本地就直接post明文啦,但在本地调试的时候也要跳过解密流程,如图

    分析得到微信回调发送的XML明文为

    <xml>
    <
    ToUserName><![CDATA[xxxxxx]]></ToUserName> <FromUserName><![CDATA[xxxx]]></FromUserName> <CreateTime>1413794769</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[cccc ]]></Content> <MsgId>4720174057645930000</MsgId> <AgentID>1</AgentID> </xml>

    所以我们建立一个XML类:RequestXMLText.cs

    代码如下

       public class RequestXMLText
        {
            public string ToUserName { get; set; }
            public string FromUserName { get; set; }
            public string CreateTime { get; set; }
            public string MsgType { get; set; }
            public string Content { get; set; }
            public string MsgId { get; set; }
            public string AgentID { get; set; }
    
    
            string xmlformat = @"<xml>
                <ToUserName><![CDATA[{0}]]></ToUserName>
                <FromUserName><![CDATA[{1}]]></FromUserName>
                <CreateTime>{2}</CreateTime>
                <MsgType><![CDATA[{3}]]></MsgType>
                <Content><![CDATA[{4} ]]></Content>
                <MsgId>{5}</MsgId>
                <AgentID>{6}</AgentID>
                </xml>";
            public string GetXML()
            {
                string Result = String.Format(xmlformat
                    , ToUserName, FromUserName, CreateTime, MsgType, Content, MsgId, AgentID);
                return Result;
            }
        }

    设计模拟微信发送消息的界面如下:

    界面设计完成以后,需要给每个xml属性定义的textbox绑定Validted事件,我为了方便起见统一处理

    在窗体load代码中写入:

            //模拟微信发送消息
                BoundXMLText XMLText = new BoundXMLText();
                XMLText.Add(txt_ToUserName, BoundXMLText.ToUserName);
                XMLText.Add(txt_FromUserName, BoundXMLText.FromUserName);
                XMLText.Add(txt_CreateTime, BoundXMLText.CreateTime);
                XMLText.Add(txt_MsgType, BoundXMLText.MsgType);
                XMLText.Add(txt_Content, BoundXMLText.Content);
                XMLText.Add(txt_MsgId, BoundXMLText.MsgId);
                XMLText.Add(txt_AgentID, BoundXMLText.AgentID);
                
                XMLText.IniNotice();
    
                XMLText.XMLChanged += XMLText_XMLChanged;
    补充 XMLText_XMLChanged事件
     void XMLText_XMLChanged(RequestXMLText xml)
            {
                txt_XMLText.Text = xml.GetXML();
            }


    BoundXMLText 类代码如下:
      public delegate void XMLChanged(RequestXMLText xml);
        public class BoundXMLText
        {
    
            public const string ToUserName ="ToUserName";
            public const string FromUserName = "FromUserName";
            public const string CreateTime = "CreateTime";
            public const string MsgType = "MsgType";
            public const string Content = "Content";
            public const string MsgId = "MsgId";
            public const string AgentID = "AgentID";
    
            public event XMLChanged XMLChanged;
    
    
            RequestXMLText xml=new RequestXMLText();
    
            public RequestXMLText TxtXML
            {
                get
                {
                    return xml;
                }
            }
    
            Dictionary<TextBox, string> lstTextBox = new Dictionary<TextBox, string>();
    
            public void Add(TextBox txt, string Attribute)
            {
                if (lstTextBox.ContainsKey(txt)) return;
    
                lstTextBox.Add(txt, Attribute);
            }
    
            public void Remove(TextBox txt)
            {
                if (lstTextBox.ContainsKey(txt))
                    lstTextBox.Remove(txt);
            }
    
            public void IniNotice()
            {
                foreach (var key in lstTextBox.Keys)
                {
                    key.Tag = lstTextBox[key];
                    key.Text = ConfigData.GetAttribute(lstTextBox[key]);
                    key.Validated += key_Validated;
                }
            }
    
            TextBox CurrentTextBox;
            void key_Validated(object sender, EventArgs e)
            {
                CurrentTextBox = (sender as TextBox);
                string attribute = ConvertEx.ToString(CurrentTextBox.Tag);
    
                SetAttribute(attribute, CurrentTextBox.Text);
    
                if (XMLChanged != null)
                    XMLChanged(TxtXML);
            }
    
            public void SetAttribute(string Attribte, string Value)
            {
                switch (Attribte)
                {
                    case ToUserName:
                        xml.ToUserName = Value; break;
                    case FromUserName:
                        xml.FromUserName = Value; break;
                    case CreateTime:
                        xml.CreateTime = Value; break;
                    case MsgType:
                        xml.MsgType = Value; break;
                    case Content:
                        xml.Content = Value; break;
                    case MsgId:
                        xml.MsgId = Value; break;
                    case AgentID:
                        xml.AgentID = Value; break;
                    default:
                        {
                            throw new Exception("没有找到对应的字段:" + Attribte);
                        }
                }
            }
    
        }

    这样就完成了text绑定,达到的目的是,光标离开textbox后,xml会自动更新

    最后写post发送事件

      private void btn_SendText_Click(object sender, EventArgs e)
            {
                string URL=GenerateURL();
                string Data=txt_XMLText.Text;
                if (String.IsNullOrEmpty(URL))
                {
                    return;
                }
                if (String.IsNullOrEmpty(Data))
                {
                    MessageBox.Show("需要Post的数据为空!,请填写内容!");
                    return;
                }
                txt_TextResult.Text = CommonTools.Post(URL, Data);
            }

    本节完成

    附上CommonTools类代码:

    public class CommonTools
        {
            /// <summary>
            /// 获得消息创建时间
            /// </summary>
            /// <returns></returns>
            public static string GetCreateTime()
            {
                return DateTime.Now.Subtract(new DateTime(1970, 1, 1, 8, 0, 0)).TotalSeconds.ToString();
            }
    
            public static string GetWebData(string URL)
            {
                String ReCode = string.Empty;
                try
                {
                    HttpWebRequest wNetr = (HttpWebRequest)HttpWebRequest.Create(URL);
                    HttpWebResponse wNetp = (HttpWebResponse)wNetr.GetResponse();
                    wNetr.ContentType = "text/html";
                    wNetr.Method = "Get";
                    Stream Streams = wNetp.GetResponseStream();
                    StreamReader Reads = new StreamReader(Streams, Encoding.UTF8);
                    ReCode = Reads.ReadToEnd();
    
                    //封闭临时不实用的资料 
                    Reads.Dispose();
                    Streams.Dispose();
                    wNetp.Close();
                }
                catch (Exception ex) { throw ex; }
    
                return ReCode;
    
            }
    
            public static string Post(string url, string data)
            {
                string returnData = null;
                try
                {
                    byte[] buffer = Encoding.UTF8.GetBytes(data);
                    HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(url);
                    webReq.Method = "POST";
                    webReq.ContentType = "application/x-www-form-urlencoded";
                    webReq.ContentLength = buffer.Length;
                    Stream postData = webReq.GetRequestStream();
                    webReq.Timeout = 99999999;
                    //webReq.ReadWriteTimeout = 99999999;
                    postData.Write(buffer, 0, buffer.Length);
                    postData.Close();
                    HttpWebResponse webResp = (HttpWebResponse)webReq.GetResponse();
                    Stream answer = webResp.GetResponseStream();
                    StreamReader answerData = new StreamReader(answer);
                    returnData = answerData.ReadToEnd();
                }
                catch (Exception ex)
                {
                    return "获取错误";
                }
                return returnData.Trim() + "
    ";
            }
    
        }

     本节完:

    日志列表:

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

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

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

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



    慎于行,敏于思!GGGGGG
  • 相关阅读:
    设计模式之策略模式
    设计模式之简单工厂模式
    UML 之关系
    C# delegate (001)
    转: 编写高质量代码改善C#程序的157个建议
    通过配置数据库邮件实现发送邮件
    存储过程学习(004)--象写程序一样的写存储过程及调试
    存储过程学习(003)--象写程序一样的写存储过程及调试
    存储过程学习(002)--循环插入数据
    jQ新的事件绑定方法on()
  • 原文地址:https://www.cnblogs.com/GarsonZhang/p/4039890.html
Copyright © 2011-2022 走看看