这几天要做一个H5的页面,之前没做。对我来说也是一次对新领域的接触。废话不多说来说说用node做服务端调用微信JS SDK遇到的坑。
首先讲一下思路:

微信JS-SDK说明文档

  1. 绑定域名

  2. 引入JS文件

  3. 通过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 + "&timestamp=" + 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的方法
       
    }
})

总结 : 这只是个示例代码的具体实现还是看场景。


caozheng550
369 声望19 粉丝

不忘初心,不畏将来!