本篇技術博客來自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官方博客」,轉載請註明出處。