本篇技术博客来自Worktile 微信之父 @龚林杰的动情分享,为您详细讲述咱们微信公众号【getworktile】背后的点点滴滴~
现现在,微信已经再也不只承担着交流沟通、娱乐大众的功能,微信公众号的推出将微信逐渐转变成我的、商家、企业单位用来营销的重要工具。而微信推出的公众号开发功能,为咱们码农带来很大的方便,让咱们创造出更多的可能性。node
今天,就结合本身的些许经验,跟你们分享下有关公众号开发的操做。而对于大部分的开发者来讲可能已经很熟悉微信公众号开发,因此这篇入门篇适用于初始了解和作公众号开发的同窗。web
Tips:本文根据微信官方文档的内容编写,并会附上实例代码(nodejs)。redis
接入微信公众平台开发,开发者须要按照以下步骤完成:算法
说明:URL是开发者用来接收微信消息和事件的接口URL,该接口尽可能写成两个请求方式,1:get请求,用于验证以下地址有效性,2:post请求, 用于接收消息和事件,Token 能够随便定义用于验证接口签名有效性, EncodingAESKey是加密的密钥,下面加密方式选兼容模式或者安全模式的时候开发者可根据该密钥对数据进行加解密
编程
根据官方文档的说明,微信验证接口会带下面几个参数json
| 参数 | 描述 |
| ------------- |:-------------:|
| signature |微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数 |
| timestamp | 时间戳 |
| nonce | 随机数 |
| echostr | 随机字符串 | segmentfault
服务端接口接收到这些参数后进行签名验证,以下代码:api
exports.check = function (req, res, next) { // 在这里验证签名 var signature = req.query['signature'], timestamp = req.query['timestamp'], nonce = req.query['nonce'], echostr = req.query['echostr']; var sha1 = crypto.createHash('sha1'), sha1Str = sha1.update([config.weixin.token, timestamp, nonce].sort().join('')).digest('hex'); res.writeHead(200, {'Content-Type': 'text/plain'}); res.end((sha1Str === signature) ? echostr : ''); return res; };
这里就是根据业务需求,进行接口调用的编程了,下面我会一一介绍安全
access_token是公众号的全局惟一接口调用凭据,公众号调用各接口时都需使用access_token。该接口一天只能请求2000次,开发者须要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将致使上次获取的access_token失效。
以下代码事例:服务器
exports.get_token = function (fn) { redis.get(weixin_token, function (err, token_str) { if (token_str) { return fn(err, JSON.parse(token_str)); } else { request.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + app_id + "&secret=" + app_secret, function (err, response, body) { if (JSON.parse(body).errcode == 45009) { return fn(err) } else { redis.set(weixin_token, body, function (err) { redis.expire(weixin_token, 7000, function () { return fn(err, JSON.parse(body)); }); }); } }); } }); };
上面的事例代码中,首先我会从redis中获取到access_token,由于我最初获取access_token的时候写入到redis中了,官方给的有效时间是7200秒,我放在redis中的有效时间是7000秒,因此我这里的token不会过时,过时后会从新调用接口获取并写入redis
实例代码中只写入建立接口的调用,查询和删除就不举例了。
以下代码事例以及说明:
get_token(function(err, obj){ var access_token = obj.access_token; request.post({ url: "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + access_token, json: { "button":[ { "type":"view", "name":"工做台", "url":"http://worktile.com" }, { "name":"解决方案", "sub_button": [ { "type":"view", "name":"研发", "url":"https://pro.worktile.com/solution/dev" } { "type":"view", "name":"最佳实践", "url":"https://worktile.com/can" } ] }, { "name":"更多", "sub_button":[ { "type":"view", "name":"下载应用", "url":"http://a.app.qq.com/o/simple.jsp?pkgname=com.worktile" }, { "type":"click", "name":"合做", "key":"work_together" }] }] } }, function(err, res, body){ console.log(body) }) });
这里get_token方法就是上面第一章中#获取access_token ,并且我这里是做为脚本执行的,这样方便之后随便修改自定义菜单内容
http请求方式:GET
https://api.weixin.qq.com/cgi...
http请求方式:GET
https://api.weixin.qq.com/cgi...
接收的消息分为普通消息和事件消息,统一有第一章中 接入指南
填写的RUL接口来接收处理
exports.receive = function (req, res, next) { // 在这接收消息 var xml = ''; req.setEncoding('utf8'); req.on('data', function (chunk) { xml += chunk; }); req.on('end', function () { toJSON(xml, res); }); };
说明:receive方法就是接收用户发给公众号的消息,内容格式是xml,toJSON就是我解析xml为json的方法,以下
//解析器 var toJSON = function (xml, res) { var msg = {}; xml2js.parseString(xml, function (err, result) { var data = result.xml; msg.ToUserName = data.ToUserName[0]; msg.FromUserName = data.FromUserName[0]; msg.CreateTime = data.CreateTime[0]; msg.MsgType = data.MsgType[0]; switch (msg.MsgType) { case 'text' : msg.Content = data.Content[0]; msg.MsgId = data.MsgId[0]; res.setHeader("Content-Type", "text/plain"); res.send(""); return handle_text(msg, res); break; case 'image' : msg.PicUrl = data.PicUrl[0]; msg.MsgId = data.MsgId[0]; msg.MediaId = data.MediaId[0]; res.setHeader("Content-Type", "text/plain"); res.send(""); break; case 'voice' : msg.MediaId = data.MediaId[0]; msg.Format = data.Format[0]; msg.MsgId = data.MsgId[0]; res.setHeader("Content-Type", "text/plain"); res.send(""); break; case 'video' : msg.MediaId = data.MediaId[0]; msg.ThumbMediaId = data.ThumbMediaId[0]; msg.MsgId = data.MsgId[0]; res.setHeader("Content-Type", "text/plain"); res.send(""); break; case 'location' : msg.Location_X = data.Location_X[0]; msg.Location_Y = data.Location_Y[0]; msg.Scale = data.Scale[0]; msg.Label = data.Label[0]; msg.MsgId = data.MsgId[0]; res.setHeader("Content-Type", "text/plain"); res.send(""); break; case 'link' : msg.Title = data.Title[0]; msg.Description = data.Description[0]; msg.Url = data.Url[0]; msg.MsgId = data.MsgId[0]; res.setHeader("Content-Type", "text/plain"); res.send(""); break; case 'event' : msg.Event = data.Event[0]; if (data.EventKey && _.isArray(data.EventKey) && data.EventKey.length > 0) { msg.EventKey = data.EventKey[0]; return handle_event(msg, res); } res.setHeader("Content-Type", "text/plain"); res.send(""); break; } }); };
说明:这里我用户发过来的消息xml解释称json数据msg, 根据不一样的类型作不一样的处理,如上文本,图片,音频,视频,连接,事件等消息
var handle_text = function (msg, res) { var text = msg.Content; if(text.trim() == "研发"){ var data = { "touser":msg.FromUserName, "msgtype":"news", "news":{ "articles": [ { "title":"重磅!Worktile 推出研发管理解决方案", "description":"项目进度清晰掌握,快速跟进产品Bug,多维度统计报表,文件文档有序管理", "url":"https://pro.worktile.com/solution/dev", "picurl":"https://wt-prj.oss.aliyuncs.com/b327e3a5666048279583e8e026ac6b87/4bb6e53c-8516-4466-b278-4f3b596e46db.png" } ] } }; sendMessageToUser(data); }else if(text.trim() == "电商"){ var data = { "touser":msg.FromUserName, "msgtype":"news", "news":{ "articles": [ { "title":"Worktile 『电商解决方案』上线!", "description":"下降运营成本,提升团队效率。平常运营、大促筹备、售后跟踪、研发管理……尽在掌握。", "url":"https://pro.worktile.com/solution/ecommerce", "picurl":"https://cdn.worktile.com/solution/ecommerce.png" } ] } }; sendMessageToUser(data); } };
说明:这是对文本消息的处理,如上,若是接收到 研发 字样的消息,公众号会给该用户发送一条新闻消息
var handle_event = function (msg, res) { console.log("weixin receive message ===", msg) if (msg.Event == 'CLICK' && msg.EventKey == 'work_together') { var text = "Hello,谢谢对 Worktile 的关注啦,请访问worktile官方网站了解。。。。。"; var data = { touser : msg.FromUserName, msgtype: "text", text : { content: text } }; sendMessageToUser(data); } };
说明:这是对事件消息的处理,如上,若是接收到 msg.Event == 'CLICK' && msg.EventKey == 'work_together' 事件的消息,公众号会给该用户发送一条文本消息,固然事件消息有不少,如:subscribe关注公众号消息,unsubscribe取消关注,扫描带参数二维码事件,还有如上说的自定义菜单事件, 上报地理位置事件等
var sendMessageToUser = function ( data) { get_token(function (err, obj) { request.post({ url : "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + obj.access_token, headers: {"Content-Type": "application/json"}, json : data }, function (err, res, body) { console.log("sendmessage....", body) }) }); };
这个发放就是调用微信接口给用户发送消息,那接下来我们就看下发送消息
发送消息分为,发送被动消息,发送客服消息,发送模版消息
客服消息,是公众号收到用户来的消息客服根据内容回复给用户的消息
若是用户并无给公众号发消息,此时客服是没法给用户发送消息的,这是微信作的一个限制
以下代码:
wtutil.get_token(function (err, obj) { //var text = "你好,这是一条消息,多谢支持..."; //var data = { // touser : "oy4hbwbd0MOMmn8aUtQWMcNxs8PI", // msgtype: "text", // text : { // content: text // } //}; var data = { touser : "oy4hbwbd0MOMmn8aUtQWMcNxs8PI", msgtype: "image", "image": { "media_id":"ZqQGrsR6ivb273zLApNfkEdAP3UI8nHJTJ9ekelfJ8OhKUF6UG-o6YbOBv4uWf4R" } }; request.post({ url : "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + obj.access_token, headers: {"Content-Type": "application/json"}, json : data }, function (err, res, body) { console.log(body); }) });
msgtype: 是消息类型,上面注释掉的是文本消息,下面是个图片消息,touser是用户的openid,这里我只是取过来直接使用的,这里跟上面接收消息后处理给用户发消息有写重复,就很少介绍了
3.模版消息你们确定很熟悉,好比Worktile的微信公众号接收任务消息通知,这样的消息就是模版消息
模版消息相对来讲复杂一下,首先要从公众号添加或者申请消息模版,以下图
那么有了消息模版以后就能够拿到模版ID,而后给用户发送模版消息了
get_token(function (err, obj) { var data = { "touser" : "oy4hbwbd0MOMmn8aUtQWMcNxs8PI", "template_id": "z6yV_lOIAM-LQbsrG-B3hTQvwt8_4Y3wVU2PH9UW16c", "url" : "https://worktile.com", "topcolor" : "#FF00FF", "data" : { "first" : { "value": "测试哈哈哈,颜色能够自定义", "color": "#33FF00" }, "one": { "value": "one", "color": "#173177" }, "two": { "value": "two", "color": "#FF0033" }, "three": { "value": "three", "color": "#173177" }, "remark" : { "value": "remark,了解更多详情,关注我。。。。", "color": "#33FF00" } } }; request.post({ url : "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + obj.access_token, headers: {"Content-Type": "application/json"}, json : data }, function (err, res, body) { console.log(body, "----") }) });
须要说明的是参数中data里面的key(first, one, two,three, remark)是模版中定义的,这里须要根据模版内容来写,还有能够设置每一个字段的颜色值等属性
到此消息管理这部分已经说的差很少了,我们继续介绍。。。
这个章节跟前面几章不一样,前面几章介绍的是公众号开发的一些东西,这个章节介绍的是网页开发,主要针对h5应用或者是页面的开发,Worktile微信版就是微信网页开发完成的,下面我们一步步的介绍。
微信网页受权采用的是Oauth2.0的受权方式:
第一步:访问以下连接获取code
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
redirect_uri是你h5地址,受权成功后会把code加入到地址上,相似于:https://weixin.worktile.com?code=xxx
这样
第二步:经过code换取网页受权access_token
请求接口(get请求)
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
返回的结果以下
{ "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE" }
access_token是用户受权的token,openid是用户对于该公众号的惟一标示,refresh_token:能够调用刷新token的接口获取最新的token
第三步:获取用户信息(需scope为 snsapi_userinfo)
请求接口(get请求)
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
经过以上3个步骤就能够获取用户的信息,进行用户的管理操做了
网页开发中,有时候咱们会自定义分享的内容,图片,音频,视频的上传,下载,地理位置,摇一摇周边,扫码,支付等的功能,这时候就须要js-sdk的开发了,下面简单介绍下js-sdk的使用,读者还能够查看官方开发文档:https://mp.weixin.qq.com/wiki...
先登陆微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
在须要调用JS接口的页面引入以下JS文件,(支持https):http://res.wx.qq.com/open/js/...
如需使用摇一摇周边功能,请引入 http://res.wx.qq.com/open/js/...
以下代码须要在网页中配置
wx.config({ debug: true, // 开启调试模式,调用的全部api的返回值会在客户端alert出来,若要查看传入的参数,能够在pc端打开,参数信息会经过log打出,仅在pc端时才会打印。 appId: '', // 必填,公众号的惟一标识 timestamp: , // 必填,生成签名的时间戳 nonceStr: '', // 必填,生成签名的随机串 signature: '',// 必填,签名,见附录1 jsApiList: [] // 必填,须要使用的JS接口列表 });
`如上代码中的timestamp, nonceStr, signature须要服务端作好签名返回给页面,这里可使用异步调用的方式,以下为服务端签名代码`
var sign = function (jsapi_ticket, url) { var ret = { jsapi_ticket: jsapi_ticket, nonceStr: createNonceStr(), timestamp: createTimestamp(), url: url }; var string = raw(ret); jsSHA = require('jssha'); shaObj = new jsSHA(string, 'TEXT'); ret.signature = shaObj.getHash('SHA-1', 'HEX'); return ret; };
`ret就是咱们须要的签名结果,其中jsapi_ticket是调用ticket接口获取的,官方文档中也有说明https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token= access_token&type=jsapi, rul是获取签名的当前网页地址, nonceStr:随机字符串,timestamp:当前10位时间戳`
var raw = function (args) { var keys = Object.keys(args); keys = keys.sort() var newArgs = {}; keys.forEach(function (key) { newArgs[key.toLowerCase()] = args[key]; }); var string = ''; for (var k in newArgs) { string += '&' + k + '=' + newArgs[k]; } string = string.substr(1); return string; };
`这个方法是对签名对象的一个字符串格式化算法`
经过ready接口处理成功验证
wx.ready(function(){
});
1. 调用接口 根据config里面 jsApiList的配置能够调用js-sdk的接口,如分享朋友圈,分享微信好友等。。。
wx.onMenuShareTimeline({
title: '', // 分享标题 link: '', // 分享连接 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 }
});
这是一个分享到朋友圈的接口,能够自定义标题,自定义连接,自定义图标` 若是想调用更多的js-sdk的接口能够参考官方文档进行开发: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN * 微信网页开发样式库 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455784134&token=&lang=zh_CN * 微信web开发者工具 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455784140&token=&lang=zh_CN 以上就是微信公众号开发的一些内容,算是入门篇。其实关于微信的开发还有不少能够作的事情,并且随着需求愈来愈多,技术愈来愈完善,相信以后关于微信的开发必定还会有更多惊喜!
Worktile 官网:worktile.com
本文做者:龚林杰
文章首发于「Worktile官方博客」,转载请注明出处。