美文网首页
微信小程序获取小程序码的过程记录

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

作者: 心情后花园 | 来源:发表于2018-11-19 20:16 被阅读0次

    上传开发代码

    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;
    };
    
    

    相关文章

      网友评论

          本文标题:微信小程序获取小程序码的过程记录

          本文链接:https://www.haomeiwen.com/subject/kwzafqtx.html