这几天要作一个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的方法 } })
总结 : 这只是个示例代码的具体实现仍是看场景。