0

0

微信公众平台SDK过程详解

Y2J

Y2J

发布时间:2017-04-27 13:49:07

|

3891人浏览过

|

来源于php中文网

原创

服务号说明:给企业和组织提供更强大的业务服务与用户管理能力,帮助企业快速实现全新的公众号服务平台。

.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
{    /// 
    /// 微信->服务器配置URL    /// 
    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((msg) =>
            {                return new ReplyTextMsg
                {
                    Content = "你说:" + msg.Content                    //FromUserName = msg.ToUserName,  默认就是这样,不用设置!                    //ToUserName = msg.FromUserName,  默认就是这样,不用设置!                    //CreateTime = DateTime.Now.Ticks     默认就是这样,不用设置!                };
            });            //注册一个用户关注的事件处理程序,当用户关注你的公众账号时,你回复“Hello!”
            WeiXin.RegisterEventHandler((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可在开发模式中获得(需要已经成为开发者,且帐号没有异常状态)。”

根据文档上说的,我们可以想到用缓存(不可能每次用每次取吧!),缓存代码是很简单的,主要是在这种情况下要能想到用缓存,下面是非完整代码:

 
       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

 二、错误码信息

佳易商城系统微信APP版
佳易商城系统微信APP版

佳易商城系统微信APP版,独立的微信公众平台管理系统;通过众平台系统,用户可以轻松管理自己的微信各类信息,对微信公众账号进行维护;含有手机客户端APP。

下载

上面说到得到凭据的代码不完整就是因为没有处理可能返回的错误码,微信错误码以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(json);
            }            else
            {
                QRCodeTicket tk = new QRCodeTicket();
                tk.error = Util.JsonTo(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>

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
{    /// 
    /// 微信->服务器配置URL    /// 
    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 handler)
{    //add handler}

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

 WeiXin.RegisterMsgHandler(MsgType.text, (recEvtMsg) => msg = recEvtMsg   ReplyTextMsg { Content =  +

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

    public static void RegisterMsgHandler(MsgType type, Func handler)
    {        //add handler    }    public static void RegisterMsgHandler(MsgType type, Func handler)
    {        //add handler    }    //.............

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

//可以RegisterMsgHandler(MsgType.text, new Func((msg) =>{    return new ReplyTextMsg { Content = "你说:" + msg.Content };
}));//可以RegisterMsgHandler(MsgType.text, new Func((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(Func 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)handler;
        }

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

相关文章

微信app下载
微信app下载

微信是一款手机通信软件,支持通过手机网络发送语音短信、视频、图片和文字。微信可以单聊及群聊,还能根据地理位置找到附近的人,带给大家全新的移动沟通体验,有需要的小伙伴快来保存下载体验吧!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

8

2026.01.15

公务员递补名单公布时间 公务员递补要求
公务员递补名单公布时间 公务员递补要求

公务员递补名单公布时间不固定,通常在面试前,由招录单位(如国家知识产权局、海关等)发布,依据是原入围考生放弃资格,会按笔试成绩从高到低递补,递补考生需按公告要求限时确认并提交材料,及时参加面试/体检等后续环节。要求核心是按招录单位公告及时响应、提交材料(确认书、资格复审材料)并准时参加面试。

44

2026.01.15

公务员调剂条件 2026调剂公告时间
公务员调剂条件 2026调剂公告时间

(一)符合拟调剂职位所要求的资格条件。 (二)公共科目笔试成绩同时达到拟调剂职位和原报考职位的合格分数线,且考试类别相同。 拟调剂职位设置了专业科目笔试条件的,专业科目笔试成绩还须同时达到合格分数线,且考试类别相同。 (三)未进入原报考职位面试人员名单。

58

2026.01.15

国考成绩查询入口 国考分数公布时间2026
国考成绩查询入口 国考分数公布时间2026

笔试成绩查询入口已开通,考生可登录国家公务员局中央机关及其直属机构2026年度考试录用公务员专题网站http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/examResult/written_result.html,查询笔试成绩和合格分数线,点击“笔试成绩查询”按钮,凭借身份证及准考证进行查询。

11

2026.01.15

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

65

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

36

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

75

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

21

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

35

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Node.js 教程
Node.js 教程

共57课时 | 8.6万人学习

Rust 教程
Rust 教程

共28课时 | 4.4万人学习

Vue 教程
Vue 教程

共42课时 | 6.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号