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的方法
       
    }
})

總結 : 這只是個示例代碼的具體實現仍是看場景。

相關文章
相關標籤/搜索