node调用微信JS SDK遇到的坑

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

微信JS-SDK说明文档node

  1. 绑定域名redis

  2. 引入JS文件express

  3. 经过config接口注入权限验证配置npm


wx.config({
        debug: true,
        appId: '', // 必填,公众号的惟一标识
        timestamp: , // 必填,生成签名的时间戳
        nonceStr: '', // 必填,生成签名的随机串
        signature: '',// 必填,签名,见附录1
        jsApiList: [] // 必填,须要使用的JS接口列表,全部JS接口列表见附录2
    });

以后就是经过ready接口处理成功验证、经过error接口处理失败验证。微信JS-SDK都有说明不作赘述。api

Node调用微信JS-SDK实践

须要安装一下模块供咱们使用: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 + "&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的方法
       
    }
})

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

相关文章
相关标签/搜索