服务端使用 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 获取带参微信小程序码图片》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Head First Design Patterns

Head First Design Patterns

Elisabeth Freeman、Eric Freeman、Bert Bates、Kathy Sierra、Elisabeth Robson / O'Reilly Media / 2004-11-1 / USD 49.99

You're not alone. At any given moment, somewhere in the world someone struggles with the same software design problems you have. You know you don't want to reinvent the wheel (or worse, a flat tire),......一起来看看 《Head First Design Patterns》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具