微信公众平台网址:https://mp.weixin.QQ.com/
服务号说明:给企业和组织提供更强大的业务服务与用户管理能力,帮助企业快速实现全新的公众号服务平台。
.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失效。公众号可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在开发模式中获得(需要已经成为开发者,且帐号没有异常状态)。”
根据文档上说的,我们可以想到用缓存(不可能每次用每次取吧!),缓存代码是很简单的,主要是在这种情况下要能想到用缓存,下面是非完整代码:
using System;using System.Collections.Generic;namespace Loogn.WeiXinSDK{ /// <summary> /// 凭据 /// </summary> [Serializable] class Credential { public string access_token { get; set; } /// <summary> /// 过期秒数 /// </summary> public int expires_in { get; set; } [NonSerialized] public DateTime add_time; static Dictionary<string, Credential> creds = new Dictionary<string, Credential>(); static string TokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}"; internal static Credential GetCredential(string appId, string appSecret) { Credential cred = null; if (creds.TryGetValue(appId, out cred)) { if (cred.add_time.AddSeconds(cred.expires_in - 30) < DateTime.Now) { creds.Remove(appId); cred = null; } else { return cred; } } var json = Util.HttpGet2(string.Format(TokenUrl, appId, appSecret)); cred = Util.JsonTo<Credential>(json); creds[appId] = cred; return cred; } }}
二、错误码信息
上面说到得到凭据的代码不完整就是因为没有处理可能返回的错误码,微信错误码以json格式返回,如:
{"errcode":40013,"errmsg":"invalid appid"}
大部分由我们主动调用的接口都有可能返回错误码,错误码格式与正常返回的数据格式完全不一样,在SDK里,我是这样处理的,先定义好错误码的模型类,我这里叫ReturnCode,是因为错误码里还包含一个{"errcode":0,"errmsg":"ok"}的请求成功的情况:
public class ReturnCode { public int errcode { get; set; } public string errmsg { get; set; } public override string ToString() { return "{ /"errcode/":" + errcode + ",/"errmsg/":/"" + 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<Dictionary<string, Dictionary<string, object>>>(json);var gi = new GroupInfo();var gpdict = dict["group"];gi.id = Convert.ToInt32(gpdict["i
新闻热点
疑难解答