這幾天要作一個H5的頁面,以前沒作。對我來講也是一次對新領域的接觸。廢話很少說來講說用node作服務端調用微信JS SDK遇到的坑。
首先講一下思路:html
微信JS-SDK說明文檔node
綁定域名redis
引入JS文件express
經過config接口注入權限驗證配置npm
wx.config({ debug: true, appId: '', // 必填,公衆號的惟一標識 timestamp: , // 必填,生成簽名的時間戳 nonceStr: '', // 必填,生成簽名的隨機串 signature: '',// 必填,簽名,見附錄1 jsApiList: [] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2 });
以後就是經過ready接口處理成功驗證、經過error接口處理失敗驗證。微信JS-SDK都有說明不作贅述。api
須要安裝一下模塊供咱們使用:promise
npm install sha1
這個哈希1模塊是處理對字符的哈希加密,生成signature緩存
npm install redis
這個你們都懂,用來作對access_token、jsapi_ticket的存儲而且設定存在時間7200s。(由於7200s之後微信返回的值纔會改變,而且微信規定天天限定2000的訪問次數);服務器
若是使用co庫的話微信
npm install co
優點
能夠減小對微信sdk服務器訪問的次數,提高性能。同時對訪問的次數限制作了最大的優化處理。
首先建一個wechatConfig.js用來存放appid,appsecret
module.exports = { appid : '',//公衆號的appId,能夠在公衆平臺上找到,-。-本身找。 appsecret : ''//公衆號的appsecret };
創建getWebToken.js 用來返回access_token,由於這個是異步返回一個promise
'use strict'; const request = require('request'); const qs = require('querystring'); const config = require('./../wechatConfig'); function getToken() { let reqUrl = 'https://api.weixin.qq.com/cgi-bin/token?'; let params = { grant_type: 'client_credential', appid: config.appid, secret: config.appsecret }; let options = { method: 'get', url: reqUrl+qs.stringify(params) }; console.log(options.url); return new Promise((resolve, reject) => { request(options, function (err, res, body) { if (res) { console.log(body) resolve(body); } else { reject(err); } }) }) } module.exports = getToken;
param的順序必定不能錯!!!這個很重要否則會證書會認證失敗!這個坑我整了1天才爬出來。
創建一個getJsApiData.js 文件用來作爲主要的controller返回結果返回給client
/** * Created by caozheng on 2016/11/24. */ 'use strict'; const fs = require('fs'); const request = require('request'); const getToken = require('./getWebToken'); const sha1 = require('sha1'); function getJsApiTicket() { return new Promise((resolve, reject) => { getToken().then(function (body) { body = JSON.parse(body); var token = body.access_token; var reqUrl = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + token + '&type=jsapi'; let options = { method: 'get', url: reqUrl }; request(options, function (err, res, body) { if (res) { resolve(body); } else { reject(err); } }) }).catch(function (err) { throw (err) }); }) } //noncestr function getNonceStr () { var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for(var i = 0; i < 16; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; } //timestamp function getTimestamp() { var time = String(new Date().valueOf()); return time.substr(0, time.length-3); } function getSign(jsApiTicket, noncestr, timestamp, url) { console.log("******************"); console.log(jsApiTicket); var sortData = "jsapi_ticket=" + jsApiTicket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url; return sha1(sortData); } //返回數據分別爲sign, timestamp, noncestr function getJsApiData(clientUrl) { let noncestr = getNonceStr(); let timestamp = getTimestamp(); return getJsApiTicket().then(data => { return [getSign(JSON.parse(data).ticket, noncestr, timestamp, clientUrl), timestamp, noncestr]; }) } module.exports = getJsApiData;
在路由處添加入口
/*微信返回sdk參數*/ router.post('/wechat',function (req, res) { var clientUrl = req.body.url; getJsApiData(clientUrl).then(data => { res.send({signature: data[0], timestamp: data[1], nonceStr: data[2]}); }); });
注意:這裏從client傳過來的url必定是動態獲取的location.href.split('#')[0],而且不能帶有#號,由於分享一篇文章以後微信會在連接後加參數。
使用redis緩存access_token、jsapi_ticket
創建一個redis.js文件,由於這裏只須要存儲功能。
/** * Created by caozheng on 2016/11/24. */ var db = {}; var redis = require('redis'); var options = { host : '', // 這裏不須要解釋吧 port : '6379', // 這裏也不須要 password : '', // 這個論英文的重要性 db : 2 //db存儲的位置 }; var client = redis.createClient(options); client.on('ready',function(err){ console.log('ready'); }); client.on("error", function (err) { console.log("Error :" , err); }); client.on('connect', function(){ console.log('Redis鏈接成功.'); }); /** * 添加string類型的數據 * @param key 鍵 * @params value 值 * @params expire (過時時間,單位秒;可爲空,爲空表示不過時) * @param callBack(err,result) */ db.set = function(key, value, expire, callback){ client.set(key, value, function(err, result){ console.log(key); console.log(value); if (err) { console.log(err); callback(err,null); return; } if (!isNaN(expire) && expire > 0) { client.expire(key, parseInt(expire)); } callback(null,result) }) }; /** * 查詢string類型的數據 * @param key 鍵 * @param callBack(err,result) */ db.get = function(key, callback){ client.get(key, function(err,result){ if (err) { console.log(err); callback(err,null); return; } callback(null,result); }); }; module.exports = db;
總結:這裏也能夠寫成異步! express可使用co庫,KOA的話那就不用看我寫的了...
在getJsApiData.js中的getJsApiTicket函數中添加redis存儲,讀取。同時須要引入redis.js
const db = require('./../../db/radis'); var res = { access_token :'', ticket : '' } // 這裏爲了和公共接口同步把數 co(function* (){ // 引用co庫 var result = yield { access_token : db.get("access_token"), ticket : db.get("ticket") } // 判斷redis中是否存在access_token、ticket if(result.access_token && result.ticket){ return result // 返回存儲中的ticket的值 }else{ // 這裏是以前代碼 -> getJsApiTicket 中返回promise的方法 } })
總結 : 這只是個示例代碼的具體實現仍是看場景。