内容简介:首先看微信小程序的获取二维码 文档,可以看到微信支持三种接口,其中只有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(); 复制代码
注意点:
-
这里比较重要的是这个
res.setEncoding("binary");
,因为服务器默认返回的数据会编码为 utf8 格式,但我们只需要二进制,且二进制转 utf8 再转回二进制貌似会丢失数据(具体我还不知道为什么)。 -
另外,这个返回的 req 对象,可以诸如
setHeader(name, value), getHeader(name), removeHeader(name)
的api,直到你使用request.end()
才真正把请求发送出去。如果你忘了调用request.end
而执行了代码,过了一段时间会报一个超时错误。 -
考虑到返回的不一定是图片,也有可能返回 JSON,所以做了一些判断。
-
如果参数比较固定,你可以把图片下载下来,将图片路径映射到 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; 复制代码
后面的话
- 原生 api 有点繁琐,建议使用一些流行的请求库,可读性高且方便修改。
- 微信 api 返回的图片流,是先获取到完整的二进制数据,再返回到客户端的。如果可以直接把传回来的每一个数据块直接发到客户端,无疑可以缩短响应时间,貌似这里可以进行优化。
- 涉及到了编码和解码的问题,这块内容要多学习。
参考
以上所述就是小编给大家介绍的《服务端使用 nodejs 获取带参微信小程序码图片》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- dotnet 获取程序所在路径的方法
- Golang:通过小程序获取微信 openid
- 如何从C程序获取NIC详细信息?
- 微信小程序与公众号获取openId的不同
- 微信小程序获取用户的 OpenId (附前端代码)
- express获取微信小程序二维码小记
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
C#图解教程
索利斯 (Daniel M.Solis) / 姚琪琳、苏林、朱晔 / 人民邮电出版社 / 2013-7-1 / CNY 89.00
本书是广受赞誉的C# 图解教程的最新版本。作者在本书中创造了一种全新的可视化叙述方式,以图文并茂的形式、朴实简洁的文字,并辅以大量表格和代码示例,全面、直观地阐述了C# 语言的各种特性。新版本除了精心修订旧版内容外,还全面涵盖了C# 5.0 的新增特性,比如异步编程、调用者信息、case 表达式、带参数的泛型构造函数、支持null 类型运算等。通过本书,读者能够快速、深入理解C#,为自己的编程生涯......一起来看看 《C#图解教程》 这本书的介绍吧!