微信小程序获取小程序码的过程记录

上传开发代码

  1. 项目本地启动,然后再 wechat 开发者工具上传
  2. 检查项目中配置的 appid,检查开发者工具里面对这个项目设置的 appid,两者都要和 小程序的appid相同,不然只能上传部分代码

生成二维码

1. 获取 access_token

根据 小程序官方文档 可以知道怎么查询,但还是有些地方需要注意

  1. 第一个参数 grant_type 的值固定为 client_credential,对于开发者而言没有什么特殊性
  2. appidsecret 都是需要登陆小程序,从微信公众平台上获取
  3. 获取到的 access_token 有效期是两个小时,没调用微信接口获取一遍,之前获取到的 access_token 的值就失效,所以需要注意保存和刷新
  4. 代码如下
/**
 * 获取小程序的 access_token
 * @param {*} originId 小程序的originId
 */
const getAccessToken = async (appid, secret) => {
  const query = {
    appid,
    secret,
    grant_type: 'client_credential'
  };
  const { data } = await request.get(WECHAT_MINI_TOKEN_URL, query);
  // 过期时间,因网络延迟等,将实际过期时间提前10秒,以防止临界点
  const expireTime = Date.now() + (data.expires_in - 10) * 1000;
  const token = data.access_token;
  return { accessToken: token, expireTime };
};

2. 获取二维码

微信开发文档中,提供三种,二维码的方式。

接口 A: 适用于需要的码数量较少的业务场景

生成小程序码,可接受 path 参数较长,生成个数受限

接口 B:适用于需要的码数量极多的业务场景

生成小程序码,可接受页面参数较短,生成个数不受限。

接口 C:适用于需要的码数量较少的业务场景

生成二维码,可接受 path 参数较长,生成个数受限

其中,接口 A 和接口 C 次数加起来,共有 100,000 个。我在项目中是使用的接口 B,所以用它来写这个例子

好了,下面就是获取二维码的正式内容了,传送门

这个获取过程中其实还是不难的,只是文档有些地方没有说明,我就说几个我在开发过程中碰到的几个常见错误

  1. access_token 的值不是放在 POST 请求的参数中,而是以 GET 请求的方式拼接在链接的后面

https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN

  1. errcode":47001,"errmsg":"data format error"

这个错误有的是说 access_token 的值失效了。然而我碰到这个错误的原因不是这个,是由于我把 access_token 放入到了 POST 请求的 body 中导致的,把 access_token 从 body 删除,错误就解决了。

  1. "errcode": 44002, "errmsg": "empty post data"

这个错误是我在 POST 请求的 body 中没有传递值导致的,主要是没有传递 scene 的值导致。

3. 转换成图片

由于第二步中微信接口返回的数据并不是图片的链接,而只是图片的二进制内容。当时给我造成挺大的困扰,因为这个接触不多,只能通过搜索找到。下面说下我的解决过程

  1. 转换成 base64 格式,然后把值传递给前端,前端把这个值放入到 imgage 标签的 src 位置就能显示出来。
/**
 * 生成小程序二维码
 * @param {*} originId 小程序的原始ID
 * @param {*} body 小程序所需参数
 */
const generateQrCode = async (originId, body) => {
  // 返回的是个对象 { accessToken: 'token 值', expireTime: '过期时间' }
  const accessToken = await ensureAccessToken(originId); // 获取 access_token 的值,
  const { data } = await request.post(
    `${WECHAT_MINI_QRCODE_UNLIMIT_URL}?access_token=${accessToken.accessToken}`,
    body,
    {
      headers: {
        'Content-Type': 'application/json' // POST 参数需要转成 JSON 字符串,不支持 form 表单提交。
      },
      responseType: 'arraybuffer' // 重点
    }
  );
  
  const base64 = Buffer.from(data).toString('base64');
  return `data:image/jpg;base64,${base64}`;
};
  1. 写入本地文件
import fs from 'fs';

/**
 * 生成小程序二维码
 * @param {*} originId 小程序的原始ID
 * @param {*} body 小程序所需参数
 */
const generateQrCode = async (originId, body) => {
  const accessToken = await ensureAccessToken(originId);
  const { data } = await request.post(
    `${WECHAT_MINI_QRCODE_UNLIMIT_URL}?access_token=${accessToken.accessToken}`,
    body,
    {
      headers: {
        'Content-Type': 'application/json'
      },
      responseType: 'stream' // 重点
    }
  );
  data.pipe(fs.createWriteStream('./qrcode.png'));
  
  // 方式二:
  // responseType: 'arraybuffer'
  // fs.writeFile('./test_origin.jpg', data, err => {
  //   console.log('data err', err);
  // });
};
  1. 以流的形式传给前端
const generateQrCode = async (originId, body) => {
  const accessToken = await ensureAccessToken(originId);
  const { data } = await request.post(
    `${WECHAT_MINI_QRCODE_UNLIMIT_URL}?access_token=${accessToken.accessToken}`,
    body,
    {
      headers: {
        'Content-Type': 'application/json'
      },
      responseType: 'arraybuffer'
    }
  );
  return Buffer.from(data);
};

const getQrcode = async ctx => {
    const qrcode = generateQrCode(originId, {});
    ctx.type = 'png';
    ctx.body = qrcode;
};

4. 完整代码

由于传给前端的过程中出现了一些问题,最终选择了第三种方法。

第一种方法传给前端后,能显示,但是有时候会扫描失败,而且开发者工具有效,真机调试实现;

第二种方法,需要把图片上传到云服务器上,然后把链接返回前端,过程比较麻烦;麻烦包括删除上传过的二维码。

第三种方法比较方便,传给小程序段显示就行。

// request.js
import axios from 'axios';
import { merge } from 'lodash';

const request = async (_options, method = 'GET') => {
  const options = merge(
    {
      headers: {
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36'
      }
    },
    { ..._options },
    {
      method
    }
  );
  return axios(options);
};

/**
 * 封装get请求
 * @param { String } url 请求路径
 * @param { Object } 请求参数
 *  params GET请求参数
 */
const get = (url, params, _options) => {
  return request({ ..._options, params, url });
};

/**
 * 封装post请求
 * @param { Object } 请求参数
 *  data POST请求请求参数,对象形式
 */
const post = (url, data, _options) => {
  return request({ ..._options, data, url }, 'POST');
};

export { get, post, request };

// index.js

import mongoose from 'mongoose';
import * as request from '../util/request';

const WECHAT_MINI_TOKEN_URL = 'https://api.weixin.qq.com/cgi-bin/token';
const WECHAT_MINI_QRCODE_UNLIMIT_URL = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit';

/**
 * 检查AccessToken是否有效,检查规则为当前时间和过期时间进行对比
 * @param {Object} token token 对象
 */
const validAccessToken = (token) => {
  const { accessToken, expireTime } = token;
  return !!accessToken && Date.now() < expireTime;
};

/**
 * 获取小程序的 access_token
 * @param {*} originId 小程序的originId
 */
const getAccessToken = async (appid, secret) => {
  const query = {
    appid,
    secret,
    grant_type: 'client_credential'
  };
  const { data } = await request.get(WECHAT_MINI_TOKEN_URL, query);
  // 过期时间,因网络延迟等,将实际过期时间提前10秒,以防止临界点
  const expireTime = Date.now() + (data.expires_in - 10) * 1000;
  const token = data.access_token;
  return { accessToken: token, expireTime };
};

/**
 * 获取有效的 access_token
 */
const ensureAccessToken = async (originId) => {
  const WechatAccount = mongoose.model('WechatAccount');
  const account = await WechatAccount.findOne({ originId: 'gh_508456022339' });
  if (!account) {
    return null;
  }
  const { accessToken, appid, secret } = account;
  if (!validAccessToken(accessToken)) {
    const token = await getAccessToken(appid, secret);

    // 刷新 wechatAccount 的 token
    account.accessToken = token;
    await account.save();

    return token;
  }
  return accessToken;
};

/**
 * 生成小程序二维码
 * @param {*} originId 小程序的原始ID
 * @param {*} body 小程序所需参数
 */
const generateQrCode = async (originId, body) => {
  const accessToken = await ensureAccessToken(originId);
  const { data } = await request.post(
    `${WECHAT_MINI_QRCODE_UNLIMIT_URL}?access_token=${accessToken.accessToken}`,
    body,
    {
      headers: {
        'Content-Type': 'application/json'
      },
      responseType: 'arraybuffer'
    }
  );
  const base64 = Buffer.from(data).toString('base64');
  return Buffer.from(data);
};

// controller.js

/**
 * 生成二维码
 */
const getQrcode = async ctx => {
  const { content, path, width = 430 } = ctx.query;
  const { originId } = ctx.header;

  const params = {
    scene: _id,
    width,
    page
  };

  const data = await ctx.services.wechatUser.generateQrCode(originId, params);
  ctx.type = 'png';
  ctx.body = data;
};

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容