服务端使用 nodejs 获取带参微信小程序码图片

栏目: Node.js · 发布时间: 5年前

内容简介:首先看微信小程序的获取二维码 文档,可以看到微信支持三种接口,其中只有B接口没有生成个数限制,长远来看,我选择使用 B 接口。根据文档,要使用 B 接口生成小程序码,就需要一个 access_token,这个 token 可以通过另一个接口传入appId和密钥来获得。详情看该接口文档。nodejs 的版本为 8.x。

首先看微信小程序的获取二维码 文档,可以看到微信支持三种接口,其中只有B接口没有生成个数限制,长远来看,我选择使用 B 接口。

根据文档,要使用 B 接口生成小程序码,就需要一个 access_token,这个 token 可以通过另一个接口传入appId和密钥来获得。详情看该接口文档。

实现

获取 access_token

nodejs 的版本为 8.x。

考虑到服务端发送的请求并不多,不打算引入 request、axios 之类的三方库,用原生 https 模块实现(其实我只是想学习 nodejs 的原生 api 哈)。

首先,要获取 access_token,要用到 appid 和 appsecret。

const https = require('https');
https.get(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${appsecret}`, res => {
    let resData = '';
    res.on('data', data => {
        resData += data;
    });
    res.on('end', () => {
        resData = JSON.parse(resData);
    })
})
复制代码

通过 end 事件,我们获得了返回的完整的 JSON 对象 resData。

如果参数正确的话,会返回 {"access_token":"ACCESS_TOKEN","expires_in":7200} 这样的 JSON 对象。expires_in 指的是 token 的有效期时间,单位是秒,获取了这个对象后,我添加了一个 timestamp 属性,存储当前时间,来确定这个 access_token 什么时候过期。这个对象,你可以存在 global 下,但最好存到 redis,这样即使你重启服务器就不需要重新获取 access_token 了。

获取小程序码图片

有了 access_token,我们就可以通过 post 请求来获取图片二进制流了。

发送 post 请求,要用到 https.request 方法,比 https.get 要复杂一点。

首先我们用自带的 url 模块,将 url 字符串转换为 URL 对象。因为我们要用到 post 方法,并指定一些headers,所以还要给这个对象追加一些属性。 url 字符串转为对象有两种方法,一种是 new URL(<urlString>) ,还有一个是 url.parse(<urlString>) 。请不要使用第一种方式,因为给转换后的对象添加属性,然后转为 JSON 对象时,不会存在(具体原因不明,有空我研究下。)第二种方式生成的对象则没有这些问题。

具体代码如下:

const url = require('url');
let options = url.parse(`https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${access_token}`);

// 添加必要的属性
options = Object.assign(options, {
    method: 'POST',
    headers: {
        // 'Content-Length': Buffer.byteLength(post_data),
        'Content-Type': 'application/json',
        'Content-Length': post_data.length,
    }
});
复制代码

这里的 post_data 其实就是请求主题里的数据。 注意获取二维码的 api 文档里的 Bug & Tip 明确说明了, POST 参数需要转成 json 字符串,不支持 form 表单提交。

const post_data = JSON.stringify({
    scene: '你要传的参数',      // 最多32个字符。
    width: 200,               // 生成的小程序码宽度。
});
复制代码

然后我们就可以用 https.request 方法去请求图片了

let req = https.request(options, (res) => {
    let resData = '';
    res.setEncoding("binary");
    res.on('data', data => {
        resData += data;
    });
    res.on('end', () => {
        // 微信api可能返回json,也可能返回图片二进制流。这里要做个判断。
        // errcode:42001 是指 token 过期了,需要重新获取。40001 是指token格式不对或很久之前的token。
        const contentType = res.headers['content-type'];
        if ( !contentType.includes('image') ) {
            console.log('获取小程序码图片失败,微信api返回的json为:')
            console.log( JSON.parse(resData) )
            return resolve(null);
        }
        const imgBuffer = Buffer.from(resData, 'binary');
        resolve( {imgBuffer, contentType} );
    });
});
req.on('error', (e) => {
    console.log('获取微信小程序图片失败')
    console.error(e);
});
req.write(post_data);   // 写入post请求的请求主体。
req.end();
复制代码

注意点:

  1. 这里比较重要的是这个 res.setEncoding("binary"); ,因为服务器默认返回的数据会编码为 utf8 格式,但我们只需要二进制,且二进制转 utf8 再转回二进制貌似会丢失数据(具体我还不知道为什么)。

  2. 另外,这个返回的 req 对象,可以诸如 setHeader(name, value), getHeader(name), removeHeader(name) 的api,直到你使用 request.end() 才真正把请求发送出去。如果你忘了调用 request.end 而执行了代码,过了一段时间会报一个超时错误。

  3. 考虑到返回的不一定是图片,也有可能返回 JSON,所以做了一些判断。

  4. 如果参数比较固定,你可以把图片下载下来,将图片路径映射到 redis 上,做个缓存。用户第二次访问的时候,直接传对应的图片就行了。

完整代码(仅供参考)

下面是完整代码和一些简单的注释,另外因为使用了 Koa 框架,需要用到 async/await 的同步方式,我把请求包装成了 Promise。

const https = require('https');
const url = require('url');

const uid = '你要传的参数';

const S_TO_MS = 1000;  // 秒到毫秒的转换。
if (!global.access_token || global.access_token.timestamp + global.access_token.expires_in * S_TO_MS <= new Date() - 300) {
    // 过期,获取新的 token
    const appid = '小程序的appId';
    const appsecret = '密钥';

    const accessTokenObj = await new Promise( (resolve, reject) =>{
        https.get(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${appsecret}`, res => {
            let resData = '';
            res.on('data', data => {
                resData += data;
            });
            res.on('end', () => {
                resolve( JSON.parse(resData) );
            })
        })
    }).catch(e => {
        console.log(e);
    });
    
    // 这里应该加一个判断的,因为可能请求失败,返回另一个 JSON 对象。
    global.access_token = Object.assign(accessTokenObj, {timestamp: +new Date()});
}

const access_token = global.access_token.access_token;

const post_data = JSON.stringify({
    scene: uid,     // 最多32个字符。
    width: 200,     // 生成的小程序码宽度。
});

let options = url.parse(`https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${access_token}`);
options = Object.assign(options, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Content-Length': post_data.length,
    }
});

// 获取图片二进制流
const {imgBuffer, contentType} = await new Promise((resolve, reject) => {
    let req = https.request(options, (res) => {
        let resData = '';
        res.setEncoding("binary");
        res.on('data', data => {
            resData += data;
        });
        res.on('end', () => {
            // 微信api可能返回json,也可能返回图片二进制流。这里要做个判断。
            const contentType = res.headers['content-type'];
            if ( !contentType.includes('image') ) {
                console.log('获取小程序码图片失败,微信api返回的json为:')
                console.log( JSON.parse(resData) )
                return resolve(null);
            }
            const imgBuffer = Buffer.from(resData, 'binary');
            resolve( {imgBuffer, contentType} );
        });
    });
    req.on('error', (e) => {
        console.log('获取微信小程序图片失败')
        console.error(e);
    });
    req.write(post_data);   // 写入 post 请求的请求主体。
    req.end();
}).catch(() => {
    return null;
});

if (imgBuffer == null) {
    ctx.body = {code: 223, msg: '获取小程序码失败'};
    return;
}
ctx.res.setHeader('Content-type', contentType);
ctx.body = imgBuffer;
复制代码

后面的话

  1. 原生 api 有点繁琐,建议使用一些流行的请求库,可读性高且方便修改。
  2. 微信 api 返回的图片流,是先获取到完整的二进制数据,再返回到客户端的。如果可以直接把传回来的每一个数据块直接发到客户端,无疑可以缩短响应时间,貌似这里可以进行优化。
  3. 涉及到了编码和解码的问题,这块内容要多学习。

参考

  1. www.cnblogs.com/chyingp/p/c…
  2. www.ruanyifeng.com/blog/2007/1…

以上所述就是小编给大家介绍的《服务端使用 nodejs 获取带参微信小程序码图片》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

YC创业营: 硅谷顶级创业孵化器如何改变世界

YC创业营: 硅谷顶级创业孵化器如何改变世界

兰德尔·斯特罗斯 (Randall Stross) / 苏健 / 浙江人民出版社 / 2014-8-1 / CNY 52.90

在互联网创业成本日益降低、融资却越来越难的今天,硅谷的Y Combinator因何成为全世界创业者趋之若鹜的创业圣地?为什么25岁左右的青年最适合创业?创业者如何才能在遴选面试中脱颖而出?为什么YC特别看好那些主要由黑客组成的创业团队? YC真的歧视女性吗?如何想出能够赢得投资的新点子?创业者应该如何寻找联合创始人? 获准进入Y Combinator及其创业公司全程跟踪批量投资项目的第一人,......一起来看看 《YC创业营: 硅谷顶级创业孵化器如何改变世界》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试