bruceli 2019-04-13
概述
微信公众平台消息接口的工作原理大概可以这样理解:从用户端到公众号端一个流程是这样的,用户发送消息到微信服务器,微信服务器将接收到的消息post到用户接入时填写的url中,在url处理程序中,首先判断消息的合法性,判断成功后根据消息体的内容做相应的处理。原理很容易理解,接触过socket的可能理解起来更容易。对于微信开发者模式的接入官网文档非常的简洁,对于初学者很多都摸不着头脑,微信官方技术文档的接入指南可以参考接入指南
大致步骤就是:
准备工作
要接入微信,首先我们需要一个微信公众号,相信做微信公众号开发的朋友都会遇到这样的问题,就是微信公众号提供的接口权限问题。如果是个人订阅号,官方提供的接口很少,很多接口都不能用。如果想测试接口功能怎么办?其实,微信公众号官方给我们提供一个微信公众测试号,测试号除了不能用微信支付接口,其他接口基本都可以用获取并且可以使用。下面我就教大家如何申请微信公众号测试号。全微信扫一扫公众平台测试账号系统登录即可,非常的方便。微信公众平台测试账号系统地址:公众平台测试账号系统登录后我们就可以轻松愉快的进行下面的玩耍了。对于常规的微信公众号接入方式与测试公众号大同小异,这儿成功了其他的类似。
填写服务器配置与验证消息的确来自微信服务器
填写服务器配置
在填写服务器配置之前我们需要了解一下微信与我们的服务器交互的过程:
当我们在微信app上,给公众号发送一条消息时,这条消息实际上是发送到了微信的服务器上,此时微信的服务器就会对消息进行封装成某种格式的数据比如xml格式,再转发到我们配置好的URL上,所以该URL实际就是我们处理数据的一个请求路径。因此该URL必须是能暴露给外界访问的一个公网地址,不能使用内网地址,生产环境可以申请腾讯云,阿里云服务器等,但在开发环境中可暂时利用一些软件来完成内网穿透以便于修改和测试,如NATAPP,花生壳等软件,使用起来也很方便,在本地安装对应的软件,配置运行后,直接使用软件分配的临时域名来访问本地应用即可,只是偶尔会存在网络不稳定的情况,具体的外网映射工具与调试的方法后面的文章我们会接受。
有了这样一个基本的概念后,我们看下接口配置信息相关的说明。
接口配置如下图所示:
对于测试号信息中的appid与appsecret两个属性是唯一的标识,每个测试号都会有自己的appid与appsecret ,是比较重要的信息,不要随意发给别人。
服务器地址URL是开发者用来接收微信消息和事件的接口URL,是我们服务器的响应微信请求的地址。
假设我们自己的服务器域名是www.rdiframework.net,准备用/WeiXin/WeChat/来接收消息,就填写:
http://www.rdiframework.net/WeiXin/WeChat/
Token可以任意填写,为了安全性和防止黑客攻击建议设置得复杂一些,并要防止泄露。
测试号一般只需要填写URL与Token两项内容,真实项目的填写还要填写EncodingAESKey,可以由开发者手动填写或随机生成,将用作消息体加解密密钥。
开发者还可选择消息加解密方式:明文模式、兼容模式和安全模式,具体可参看开发者文档。
另请注意,微信公众号接口地址必须以http://或https://开头,分别支持80端口和443端口。
当我们填入url与token的值,并提交后,微信会发送一个get请求到我们填写的url上,并且携带4个参数,而signature参数结合了开发者填写的token参数和请求中的timestamp参数、nonce参数来做的加密签名,我们在后台需要对该签名进行校验,看是否合法。实际上,我们发现微信带过来的4个参数中并没有带token参数,仅有signature是和token有关的,所以我们应该在本地应用中也准备一个和填入的token相同的参数,再通过微信传入的timestamp与nonce做相同算法的加密操作,若结果与微信传入的signature相同,即为合法,则原样返回echostr参数,代表接入成功,否则不做处理,则接入失败。
详细流程可参考微信官方提供的逻辑流程图,我们的应用需要以该流程图的步骤来实现。
下面为我们以.NET MVC代码实现get请求样例代码,可供参考:
[HttpGet] [ActionName("Index")] public Task<ActionResult> Get(string signature, string timestamp, string nonce, string echostr) { return Task.Factory.StartNew(() => { if (CheckSignature.Check(signature, timestamp, nonce, "WeiXinToken")) { return echostr; //返回随机字符串则表示验证通过 } else { return "failed:" + signature + "," + CheckSignature.GetSignature(timestamp, nonce, weixinOfficialAccountEntity.Token) + "。" + "如果你在浏览器中看到这句话,说明此地址可以被作为微信公众账号后台的Url,请注意保持Token一致。"; } }).ContinueWith<ActionResult>(task => Content(task.Result)); }
上面的代码注意token不是微信服务器发过来的,而是我们自己写死的一个常量,就是在微信后台填写的Token,这儿我填写的是:WeiXinToken
验证URL有效性成功后我们就可以接收来自微信的数据,并对接收到的数据按需做相应的业务处理。用户每次向公众号发送消息、或者产生自定义菜单、或产生微信支付订单等情况时,开发者填写的服务器配置URL将得到微信服务器推送过来的消息和事件,开发者可以依据自身业务逻辑进行响应,如回复消息。
公众号调用各接口时,一般会获得正确的结果,具体结果可见对应接口的说明。返回错误时,可根据返回码来查询错误原因。全局返回码说明
微信后台发送消息是一个POST请求,但和普通的POST请求不同的是,首先,URL会带上signature、timestamp、nonce这3个参数:
POST http://www.rdiframework.net/WeiXin/WeChat/?signature=xxx×tamp=123456&nonce=123
然后,HTTP请求的BODY是一个不规范的XML:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[this is a test]]></Content> <MsgId>1234567890123456</MsgId> </xml>
我们自己的服务器只需要处理该XML,然后,向微信返回一个类似如下的XML:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[你好]]></Content> </xml>
就可以完成消息的回复。微信后台要求必须在5秒内回复,最多重试3次,否则我们自己的回复消息就到达不了用户的手机了。如果我们自己的服务器无法在5秒内回复,就回复一个空字符串,告诉微信服务器,不用重试了,这个消息处理不了,不给用户回复了。
上面的交互逻辑看起来很简单,但实际上坑有很多。
首先,微信服务器发送的POST请求根本就不符合HTTP规范。原则上POST请求不应该在URL上附带参数,但微信后台偏偏要这么干,这就让很多编程语言的标准框架无法获取到POST参数,因为标准的POST参数是从HTTP BODY中解析的。所以从POST获取URL参数就需要用到更底层的代码。现在无论做什么都讲究的是效率,什么东西有现成的我们就不需要去重复的造轮子,直接拿来使用即可。在.net下微信开发我们推荐使用开源的SENPARC.WEIXIN SDK,这个sdk基本完成了微信全系列的操作功能,使用的客户多,一直在升级中,可放心使用。简单的几行代码就可以实现一个功能,何乐而不为呢。
接收微信的请求代码参考:
[HttpPost] [ActionName("Index")] public Task<ActionResult> Post(PostModel postModel) { return Task.Factory.StartNew<ActionResult>(() => { if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, "WeiXinToken")) { return new WeixinResult("参数错误!"); } postModel.Token = "****Token***"; postModel.EncodingAESKey = "***EncodingAESKey***"; //根据自己后台的设置保持一致 postModel.AppId = "****AppId***"; //根据自己后台的设置保持一致 //自定义MessageHandler,对微信请求的详细判断操作都在这里面。 var messageHandler = new CustomMessageHandler(Request.InputStream, postModel,10); messageHandler.Execute(); //执行微信处理过程 return new FixWeixinBugWeixinResult(messageHandler); }).ContinueWith<ActionResult>(task => task.Result); }
吐槽:微信和微信公众平台虽然产品很先进,但后台API设计得确实不咋地。由于API是给开发人员使用的,所以,设计一个好的API要从开发人员的角度出发。与其使用笨重的XML,不如使用更符合Web潮流的JSON。
例如验证服务器:
{ "signature": "xxx", "timestamp": 123456, "nonce": "xxx", "action": "verify", "data": { "echostr": "echo" } }
这样设计的API,各种编程语言都能处理,而且处理逻辑更简单,速度更快。
RDIFramework.NET — 基于.NET的快速信息化系统开发框架 — 系列目录
RDIFramework.NET ━ .NET快速信息化系统开发框架 ━ 工作流程组件介绍
RDIFramework.NET框架SOA解决方案(集Windows服务、WinForm形式与IIS形式发布)-分布式应用
RDIFramework.NET代码生成器全新V3.5版本发布-重大升级
一路走来数个年头,感谢RDIFramework.NET框架的支持者与使用者,大家可以通过下面的地址了解详情。
RDIFramework.NET官方网站:http://www.rdiframework.net/
RDIFramework.NET官方博客:http://blog.rdiframework.net/
同时需要说明的,以后的所有技术文章以官方网站为准,欢迎大家收藏!
RDIFramework.NET框架由专业团队长期打造、一直在更新、一直在升级,请放心使用!
欢迎关注RDIFramework.net框架官方公众微信(微信号:guosisoft),及时了解最新动态。
扫描二维码立即关注