微信小程序獲取小程序碼的過程記錄

上傳開發(fā)代碼

  1. 項目本地啟動藕届,然后再 wechat 開發(fā)者工具上傳
  2. 檢查項目中配置的 appid,檢查開發(fā)者工具里面對這個項目設(shè)置的 appid痛倚,兩者都要和 小程序的appid相同,不然只能上傳部分代碼

生成二維碼

1. 獲取 access_token

根據(jù) 小程序官方文檔 可以知道怎么查詢,但還是有些地方需要注意

  1. 第一個參數(shù) grant_type 的值固定為 client_credential上渴,對于開發(fā)者而言沒有什么特殊性
  2. appidsecret 都是需要登陸小程序,從微信公眾平臺上獲取
  3. 獲取到的 access_token 有效期是兩個小時喜颁,沒調(diào)用微信接口獲取一遍稠氮,之前獲取到的 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);
  // 過期時間半开,因網(wǎng)絡(luò)延遲等隔披,將實際過期時間提前10秒,以防止臨界點
  const expireTime = Date.now() + (data.expires_in - 10) * 1000;
  const token = data.access_token;
  return { accessToken: token, expireTime };
};

2. 獲取二維碼

微信開發(fā)文檔中寂拆,提供三種奢米,二維碼的方式。

接口 A: 適用于需要的碼數(shù)量較少的業(yè)務(wù)場景

生成小程序碼纠永,可接受 path 參數(shù)較長鬓长,生成個數(shù)受限

接口 B:適用于需要的碼數(shù)量極多的業(yè)務(wù)場景

生成小程序碼,可接受頁面參數(shù)較短尝江,生成個數(shù)不受限涉波。

接口 C:適用于需要的碼數(shù)量較少的業(yè)務(wù)場景

生成二維碼,可接受 path 參數(shù)較長,生成個數(shù)受限

其中啤覆,接口 A 和接口 C 次數(shù)加起來苍日,共有 100,000 個。我在項目中是使用的接口 B窗声,所以用它來寫這個例子

好了相恃,下面就是獲取二維碼的正式內(nèi)容了,傳送門

這個獲取過程中其實還是不難的嫌佑,只是文檔有些地方?jīng)]有說明豆茫,我就說幾個我在開發(fā)過程中碰到的幾個常見錯誤

  1. access_token 的值不是放在 POST 請求的參數(shù)中,而是以 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 中導(dǎo)致的,把 access_token 從 body 刪除炮温,錯誤就解決了火脉。

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

這個錯誤是我在 POST 請求的 body 中沒有傳遞值導(dǎo)致的,主要是沒有傳遞 scene 的值導(dǎo)致柒啤。

3. 轉(zhuǎn)換成圖片

由于第二步中微信接口返回的數(shù)據(jù)并不是圖片的鏈接倦挂,而只是圖片的二進制內(nèi)容。當(dāng)時給我造成挺大的困擾担巩,因為這個接觸不多方援,只能通過搜索找到。下面說下我的解決過程

  1. 轉(zhuǎn)換成 base64 格式涛癌,然后把值傳遞給前端犯戏,前端把這個值放入到 imgage 標(biāo)簽的 src 位置就能顯示出來。
/**
 * 生成小程序二維碼
 * @param {*} originId 小程序的原始ID
 * @param {*} body 小程序所需參數(shù)
 */
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 參數(shù)需要轉(zhuǎn)成 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 小程序所需參數(shù)
 */
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. 完整代碼

由于傳給前端的過程中出現(xiàn)了一些問題弃衍,最終選擇了第三種方法呀非。

第一種方法傳給前端后,能顯示镜盯,但是有時候會掃描失敗岸裙,而且開發(fā)者工具有效,真機調(diào)試實現(xiàn)速缆;

第二種方法降允,需要把圖片上傳到云服務(wù)器上,然后把鏈接返回前端激涤,過程比較麻煩;麻煩包括刪除上傳過的二維碼。

第三種方法比較方便倦踢,傳給小程序段顯示就行送滞。

// 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 } 請求參數(shù)
 *  params GET請求參數(shù)
 */
const get = (url, params, _options) => {
  return request({ ..._options, params, url });
};

/**
 * 封裝post請求
 * @param { Object } 請求參數(shù)
 *  data POST請求請求參數(shù),對象形式
 */
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是否有效辱挥,檢查規(guī)則為當(dāng)前時間和過期時間進行對比
 * @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);
  // 過期時間犁嗅,因網(wǎng)絡(luò)延遲等,將實際過期時間提前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 小程序所需參數(shù)
 */
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;
};

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末褂微,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子园爷,更是在濱河造成了極大的恐慌宠蚂,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件童社,死亡現(xiàn)場離奇詭異求厕,居然都是意外死亡,警方通過查閱死者的電腦和手機扰楼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門呀癣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人弦赖,你說我怎么就攤上這事项栏。” “怎么了蹬竖?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵沼沈,是天一觀的道長。 經(jīng)常有香客問我案腺,道長庆冕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任劈榨,我火速辦了婚禮访递,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘同辣。我一直安慰自己拷姿,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布旱函。 她就那樣靜靜地躺著响巢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棒妨。 梳的紋絲不亂的頭發(fā)上踪古,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音,去河邊找鬼伏穆。 笑死拘泞,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的枕扫。 我是一名探鬼主播陪腌,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼烟瞧!你這毒婦竟也來了诗鸭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤参滴,失蹤者是張志新(化名)和其女友劉穎强岸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卵洗,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡请唱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了过蹂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片十绑。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖酷勺,靈堂內(nèi)的尸體忽然破棺而出本橙,到底是詐尸還是另有隱情,我是刑警寧澤脆诉,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布甚亭,位于F島的核電站,受9級特大地震影響击胜,放射性物質(zhì)發(fā)生泄漏亏狰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一偶摔、第九天 我趴在偏房一處隱蔽的房頂上張望暇唾。 院中可真熱鬧,春花似錦辰斋、人聲如沸策州。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽够挂。三九已至,卻和暖如春藕夫,著一層夾襖步出監(jiān)牢的瞬間孽糖,已是汗流浹背枯冈。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留办悟,地道東北人霜幼。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像誉尖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子铸题,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容