Home > Article > WeChat Applet > Detailed explanation of the WeChat public platform SDK process
Service account description: Provide enterprises and organizations with more powerful business services and user management capabilities, and help enterprises quickly implement a new public account service platform.
.NETSDK: Loogn.WeiXinSDK (net2.0 source code, the following code is only approximate, not very accurate, please download the source code yourself)
Since I still use NOKIA-C5, I have never used it. WeChat, I definitely don’t know as much about WeChat as you do, but the company has needs, so I have to bite the bullet and read the interface documentation directly.
After reading it, I found that it is quite interesting. A very useful function is that when a user sends a message to a public account, the program can automatically reply to the user based on the content sent by the user, such as sending a message to the public account of a logistics company. A waybill number,
The other party will automatically reply to you with the logistics details of this waybill number, which feels very cool! For the convenience of explanation, the applied public account information is first given:
The following figure shows the detailed message process of viewing the logistics above (the numbers in the dotted lines indicate the order of the process):
WeChat will send two major types of messages to your URL:
The first is the user’s general message, such as the waybill number sent by the user above;
The second is the user's behavior (that is, the events mentioned in the document), such as the user following your public account, scanning the QR code of the public account, clicking on your customized menu, etc.
Your URL can respond based on the message type and content received to achieve powerful business services, such as the logistics details returned above. All messages are delivered in XML format, and what the SDK does is convert XML into .NET objects to facilitate you writing business logic. The framework class diagram of the message is represented as (click to view the full diagram including subclasses):
First there is a message base class, then the received message (RecEventBaseMsg) and Reply message (ReplyBaseMsg), as mentioned above, received messages are divided into two categories, namely general messages (RecBaseMsg) and event messages (EventBaseMsg). The received message type can be represented by an enumeration:
Not to mention other types, when MsgType is Event, the message is a subclass of EventBaseMsg. The MsgType of all subclasses of EventBaseMsg is Event, so the EventBaseMsg type also has an EventType. To distinguish different events, if you have read the interface document, you should know that its event type is not very friendly for us to determine which event it is. The scanning QR code event is divided into two situations: the user has paid attention and the user has not paid attention. When paying attention, the EventType is scan, when not paying attention, the EventType is subscribe, and the EventType of the event that the user pays attention to is also subscribe, so another MyEventType is added to the SDK:
Now the message process It’s basically clear. The reply message when calling the SDK is as follows:
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; } } } }
The SDK contains the encapsulation of all interfaces except (OAuth2.0 web authorization). The class names and method names are very obvious, so I will not demonstrate them one by one here. Interested friends can download the dll and test it by themselves. This is a paid and certified interface diagram:
Let’s talk about a few details of the implementation:
1. Credential (access_token) expiration
"Access_token is the globally unique ticket of the public account. The public account needs to use access_token when calling each interface. Under normal circumstances, access_token is valid for 7200 seconds , repeated acquisition will cause the last access_token to be invalid. The public account can use AppID and AppSecret to call this interface to obtain access_token. AppID and AppSecret can be obtained in development mode (you need to be a developer, and the account has no abnormal status).
According to what the document says, we can think of using cache (it’s impossible to use it every time!). The cache code is very simple, mainly in this situation. Use cache, the following is Incomplete code:
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>
2. Error code information
It is mentioned above that the code to obtain the credentials is incomplete because there is no processing possibility Returned error code, WeChat error code is returned in json format, such as:
{"errcode":40013,"errmsg":"invalid appid"}
Most of the interfaces actively called by us may return error codes. The error code format is completely different from the normally returned data format. In the SDK, I handle it like this. First define the model class of the error code. I call it ReturnCode because the error code also contains an {"errcode":0,"errmsg":"ok"} request success. Situation:
errcode { ; errmsg { ; + errcode + + errmsg +
When defining a return message class with an error code, we can include an attribute of the ReturnCode type, such as creating a QR code interface:
public class QRCodeTicket { public string ticket { get; set; } public int expire_seconds { get; set; } public ReturnCode error { get; set; } }
From the returned json to QRCodeTicket The code of the object is probably like this (others are similar):
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; }
So after calling the interface with the SDK, the obtained object can be easily judged:
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表达式注册。
The above is the detailed content of Detailed explanation of the WeChat public platform SDK process. For more information, please follow other related articles on the PHP Chinese website!