这几天要做一个H5的页面,之前没做。对我来说也是一次对新领域的接触。废话不多说来说说用node做服务端调用微信JS SDK遇到的坑。
首先讲一下思路:
绑定域名
引入JS文件
通过config接口注入权限验证配置
wx.config({
debug: true,
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
之后就是通过ready接口处理成功验证、通过error接口处理失败验证。微信JS-SDK都有说明不做赘述。
Node调用微信JS-SDK实践
需要安装一下模块供我们使用:
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的方法
}
})
总结 : 这只是个示例代码的具体实现还是看场景。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。