微信公眾平台SDK過程詳解

Y2J
發布: 2017-04-27 13:49:07
原創
3492 人瀏覽過

服務號碼說明:提供企業和組織更強大的業務服務與使用者管理能力,幫助企業快速實現全新的公眾號服務平台。

.NETSDK:Loogn.WeiXinSDK (net2.0原始碼,下面程式碼只是大概,不太正確,請自行下載原始碼)

由於本人用的還是NOKIA-C5,沒用過微信,對微信的了解肯定沒你多,但公司有需求,只好硬著頭皮直接看接口文件了。

看後發現也挺有趣的,一個很有用的作用就是,當用戶給公眾帳號發送訊息時,程式可以根據用戶發送的內容自動回覆用戶,例如給一個物流公司的公眾帳號發個運單號,

對方自動回覆你這個運單號的物流詳細,感覺挺酷!為了說明方便,先給予申請好的公眾帳號資訊:

下圖為表示上面查看物流詳細的訊息流程(虛線的編號表示流程的順序):

 

微信會向你的URL發送兩大類訊息:

一是用戶的一般訊息,如上面用戶發送的單號;

二是使用者的行為(即文件中所說的事件)  ,如使用者追蹤了你的公用帳號、掃描了公有帳號的二維碼、點選了你自訂的選單等。

 你的URL就可以根據收到的訊息類型和內容做出回應以實現強大的業務服務,如上面返回的物流詳細。訊息全部是以XML格式傳遞,而SDK做的就是把XML轉換成.NET對象,以便你寫業務邏輯。訊息的框架類別圖表示為(點擊查看包含子類別的全圖):

 首先有個訊息基類,然後是收到的訊息(RecEventBaseMsg)和回覆的訊息(ReplyBaseMsg),上面說了,收到的訊息分成兩大類,即一般訊息(RecBaseMsg)和事件訊息(EventBaseMsg),收到的訊息類型用枚舉表示可以是:

其他的型別不說,而當MsgType為Event時,訊息就是EventBaseMsg的子類別了,所有EventBaseMsg的子類別的MsgType都是Event,所以EventBaseMsg型別又有個EventType來區分不同的事件,如果你看過接口文檔,你應該知道,它的事件類型對我們判斷到底是哪個事件不太友好,掃描二維碼事件分了用戶已關注和未關注兩種情況,已關注時EvenType是scan,未關注時EventType是subscribe,而用戶關注事件的EventType也是subscribe,所以SDK裡又加了個MyEventType:

現在訊息的流程基本上清楚了,呼叫SDK回覆訊息如下:

using System.Web;using Loogn.WeiXinSDK;using Loogn.WeiXinSDK.Message;namespace WebTest
{    /// <summary>
    /// 微信->服务器配置URL    /// </summary>
    public class WeiXinAPI : IHttpHandler
    {        static string Token = "Token";//这里是Token不是Access_Token
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";            var signature = context.Request["signature"];            var timestamp = context.Request["timestamp"];            var nonce = context.Request["nonce"];            if (WeiXin.CheckSignature(signature, timestamp, nonce, Token))//验证是微信给你发的消息            {                //根据注册的消息、事件处理程序回复,                //如果得到没用注册的消息或事件,会返回ReplyEmptyMsg.Instance,即GetXML()为string.Empty,符合微信的要求
                var replyMsg = WeiXin.ReplyMsg();                var xml = replyMsg.GetXML();                //WriteLog(xml); //这里可以查看回复的XML消息                context.Response.Write(xml);
            }            else
            {
                context.Response.Write("fuck you!");
            }
        }        static WeiXinAPI()
        {
            WeiXin.ConfigGlobalCredential("appid", "appSecret");            //注册一个消息处理程序,当用户发"ABC",你回复“你说:ABC”;
            WeiXin.RegisterMsgHandler<RecTextMsg>((msg) =>
            {                return new ReplyTextMsg
                {
                    Content = "你说:" + msg.Content                    //FromUserName = msg.ToUserName,  默认就是这样,不用设置!                    //ToUserName = msg.FromUserName,  默认就是这样,不用设置!                    //CreateTime = DateTime.Now.Ticks     默认就是这样,不用设置!                };
            });            //注册一个用户关注的事件处理程序,当用户关注你的公众账号时,你回复“Hello!”
            WeiXin.RegisterEventHandler<EventAttendMsg>((msg) =>
            {                return new ReplyTextMsg
                {
                    Content = "Hello !"
                };
            });            //还可以继续注册你感兴趣的消息、事件处理程序        }        public bool IsReusable
        {            get
            {                return false;
            }
        }
    }
}
登入後複製

 SDK包含了除(OAuth2.0網頁授權)的所有介面的封裝,類別名稱及方法名稱都很明顯,這裡就不一一演示,有興趣的朋友可以下載dll自行測試,這是一張付費認證過的介面圖:

#接下來談談實作的幾個細節:

一、憑證(access_token)過期

「access_token是公眾號的全域唯一票據,公眾號呼叫各介面時都需使用access_token。正常情況下access_token有效期限為7200秒 ,重複取得將導致上次取得的access_token失效。

根據文件上說的,我們可以想到用快取(不可能每次用每次取吧!),快取程式碼是很簡單的,主要是在這種情況下要能想到用緩存,下面是非完整程式碼:

 
       access_token { ;  
         
          expires_in { ;  Dictionary<, Credential> creds =  Dictionary<, Credential>  TokenUrl =   Credential GetCredential( appId, =  (creds.TryGetValue(appId,  (cred.add_time.AddSeconds(cred.expires_in - ) <=  json = Util.HttpGet2(= Util.JsonTo<Credential>
登入後複製

 二、錯誤碼訊息

上面說到得到憑證的程式碼不完整就是因為沒有處理可能傳回的錯誤碼,微信錯誤碼以json格式傳回,如:

{"errcode":40013,"errmsg":"invalid appid"}
登入後複製

大部分由我們主動呼叫的介面都有可能傳回錯誤碼,錯誤碼格式與正常回傳的資料格式完全不一樣,在SDK裡,我是這樣處理的,先定義好錯誤碼的模型類,我這裡叫ReturnCode,是因為錯誤碼裡還包含一個{"errcode":0,"errmsg":"ok"}的請求成功的情況:

       errcode { ;   errmsg { ;     + errcode +  + errmsg +
登入後複製

定義有錯誤碼的回傳訊息類別時我們就可以包含一個ReturnCode類型的屬性了,如建立二維碼介面:

    public class QRCodeTicket
    {        public string ticket { get; set; }        public int expire_seconds { get; set; }        public ReturnCode error { get; set; }
    }
登入後複製

從傳回的json到QRCodeTicket物件的程式碼大概就是這樣(其他的也是類似):

            var json = Util.HttpPost2(url, data);            if (json.IndexOf("ticket") > 0)
            {                return Util.JsonTo<QRCodeTicket>(json);
            }            else
            {
                QRCodeTicket tk = new QRCodeTicket();
                tk.error = Util.JsonTo<ReturnCode>(json);                return tk;
            }
登入後複製

所以用SDK呼叫介面後,得到的物件就可輕鬆判斷了:

            var qrcode = WeiXin.CreateQRCode(true, 23);            if (qrcode.error == null)
            {                //返回错误,可以用qrcode.error查看错误消息            }            else
            { 
                //返回正确,可以操作qrcode.ticket
            }
登入後複製

三、反序列化

微信接口返回的json有时候对我们映射到对象并不太直接(json格式太灵活了!),比如创建分组成功后返回的json:

{    "group": {        "id": 107, 
        "name": "test"
    }
}
登入後複製

如果想直接用json通过反序列化得到对象,那么这个对象的类的定义有可能会是这样:

    public class GroupInfo
    {        public Group group { get; set; }        public class Group
        {            public int id { get; set; }            public string name { get; set; }
        }
    }
登入後複製

访问的时候也会是gp.group.name,所以我说不太直接,我们想要的类的定义肯定是只有上面那个子类的样子:

    public class GroupInfo
    {            public int id { get; set; }            public string name { get; set; }
    }
登入後複製

如果微信接口返回的是这样:

    {        "id": 107, 
        "name": "test"
    }
登入後複製

就再好不过了,但人家的代码,我们修改不了,我们只有自己想办法.

1,要简单类,2不手动分析json(如正则),3,不想多定义一个类,你有想到很好的方法吗?如果有可以回复给我,而我选择用字典来做中间转换。

因为基本所有的json格式都可以反序列化为字典(嵌套字典,嵌套字典集合等),比如上面微信返回的json就可以用以下的类型来表示:

Dictionary<string, Dictionary<string, object>>
登入後複製

json--->dict--->GroupInfo

var dict = Util.JsonTo(json);var gi = new GroupInfo();var gpdict = dict["group"];
gi.id = Convert.ToInt32(gpdict["id"]);
gi.name = gpdict["name"].ToString();
登入後複製

四、消息处理的优化

"万物简单为美",我就是一个非常非常喜欢简单的程序员。还记得最开始的那个消息(事件属于消息,这里统称为消息)处理吧,我感觉是很简单的,需要处理哪个消息就注册哪个消息的处理程序。但一开始的时候不是这样的,开始的时候要手动判断消息类型,就像:

using System.Web;using Loogn.WeiXinSDK;using Loogn.WeiXinSDK.Message;namespace WebTest
{    /// <summary>
    /// 微信->服务器配置URL    /// </summary>
    public class WeiXinAPI : IHttpHandler
    {        static string Token = "Token";//这里是Token不是Access_Token
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";            var signature = context.Request["signature"];            var timestamp = context.Request["timestamp"];            var nonce = context.Request["nonce"];            if (WeiXin.CheckSignature(signature, timestamp, nonce, Token))//验证是微信给你发的消息            {                var replyMsg = WeiXin.ReplyMsg((recEvtMsg) =>
                {                    switch (recEvtMsg.MsgType)
                    {                        case MsgType.text:
                            {                                var msg = recEvtMsg as RecTextMsg; //这里要转型,麻烦
                                return new ReplyTextMsg
                                {
                                    Content = "你说:" + msg.Content
                                };
                            }                        case MsgType.Event:
                            {                                var evtMsg = recEvtMsg as EventBaseMsg;//这里要转型到事件消息的基本,麻烦
                                switch (evtMsg.MyEventType)
                                {                                    case MyEventType.Attend:                                        var msg = evtMsg as EventAttendMsg;//这个例子不需要这行代码,但其他要用消息内容还是要转型,麻烦
                                        return new ReplyTextMsg
                                        {
                                            Content = "Hello !"
                                        };                                        
                                    default:                                        break;
                                }                                break;
                            }                        default:                            break;
                    }                    return ReplyEmptyMsg.Instance;                    //嵌套switch,而且每个case都有好几个,这也不优雅                });                var xml = replyMsg.GetXML();                //WriteLog(xml); //这里可以查看回复的XML消息                context.Response.Write(xml);
            }            else
            {
                context.Response.Write("fuck you!");
            }
        }        public bool IsReusable
        {            get
            {                return false;
            }
        }
    }
}
登入後複製

做优化的时候,先是试着看能不能在MsgType和MyEventType上做文章,比如注册时传入MsgType和处理程序(lamba)两个参数:

public static void RegisterMsgHandler(MsgType type, Func<RecEventBaseMsg, ReplyBaseMsg> handler)
{    //add handler}
登入後複製

 这样的确是可以行的通的,但是在调用SDK注册的时候还是要手动转换类型:

 WeiXin.RegisterMsgHandler(MsgType.text, (recEvtMsg) => msg = recEvtMsg   ReplyTextMsg { Content =  +
登入後複製

 那么能不能每个子类型写一个呢?

    public static void RegisterMsgHandler(MsgType type, Func<RecTextMsg, ReplyBaseMsg> handler)
    {        //add handler    }    public static void RegisterMsgHandler(MsgType type, Func<RecImageMsg, ReplyBaseMsg> handler)
    {        //add handler    }    //.............
登入後複製

 定义是可以的,来看看调用:

//可以RegisterMsgHandler(MsgType.text, new Func<RecTextMsg, ReplyBaseMsg>((msg) =>{    return new ReplyTextMsg { Content = "你说:" + msg.Content };
}));//可以RegisterMsgHandler(MsgType.text, new Func<RecImageMsg, ReplyBaseMsg>((msg) =>{    return new ReplyTextMsg { Content = "你发的图片:" + msg.PicUrl };
}));//可以,注意这里msg的智能提示是RecTextMsg类型RegisterMsgHandler(MsgType.text, (msg) =>{    return new ReplyTextMsg { Content = "你说:" +msg.Content};
});//可以,注意这里msg的智能提示还是RecTextMsg类型,但用了类型推断,运行时可以确定是RecImageMsg,所以可以RegisterMsgHandler(MsgType.text, (msg) =>{    return new ReplyTextMsg { Content = "你发的图片:" + msg.PicUrl };
});//不可以,注意这里msg的智能提示还是RecTextMsg类型,但lamba body里没有用msg的特定子类的属性,类型推断不了,所以调用不明RegisterMsgHandler(MsgType.text, (msg) =>{    return new ReplyTextMsg { Content = "你发了个消息" };
});
登入後複製

 从上面调用可知,想用这种方法调用,就不能随意的用lamba表达式,我所不欲也!最后,终于用泛型搞定了

public static void RegisterMsgHandler<TMsg>(Func<TMsg, ReplyBaseMsg> handler) where TMsg : RecBaseMsg
        {            var type = typeof(TMsg);            var key = string.Empty;            if (type == typeof(RecTextMsg))
            {
                key = MsgType.text.ToString();
            }            else if (type == typeof(RecImageMsg))
            {
                key = MsgType.image.ToString();
            }            else if (type == typeof(RecLinkMsg))
            {
                key = MsgType.link.ToString();
            }            else if (type == typeof(RecLocationMsg))
            {
                key = MsgType.location.ToString();
            }            else if (type == typeof(RecVideoMsg))
            {
                key = MsgType.video.ToString();
            }            else if (type == typeof(RecVoiceMsg))
            {
                key = MsgType.voice.ToString();
            }            else
            {                return;
            }
            m_msgHandlers[key] = (Func<RecEventBaseMsg, ReplyBaseMsg>)handler;
        }
登入後複製

经过这样的变换,我们才可以像开始那样用简洁的lamba表达式注册。

以上是微信公眾平台SDK過程詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板