用於接口調用的一個必要參數express
有了 access_token 就能實現全部的接口npm
1. 有效期爲 2 小時,因此 2 小時要更新一次,提早 5 分鐘更新(確保後續正常使用)編程
2. 若是重複獲取,會致使上一次失效(須要 appid 和 appsecret 來獲取)json
3. access_token 存儲至少要保留 512 個字符空間api
4. 接口調用有限制,普通 2000次/天,測試號200次/天數組
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRETpromise
正常狀況下,微信會返回下述JSON數據包給公衆號:服務器
{"access_token":"ACCESS_TOKEN", "expires_in":7200}微信
全局返回碼,參見
1. 第一次發送請求,獲取 access_token,保存在未來 2 小時內使用
2. 之後發送請求,讀取上一次保存的 access_token,判斷是否過時
過時了,從新發送請求獲取 access_token
沒有過時,直接使用
優化爲 getValidAccessToken():
直接 redAccessToken() 讀取 access_token,判斷是否過時 isValidAccessToken()
讀取成功:
沒過時,直接用
過時,發送請求 requestAccessToken() 獲取 access_token,saveAccessToken()
讀取失敗
發送請求 requestAccessToken() 獲取 access_token,saveAccessToken()
class WeChat {
async requestAccessToken(){
// 定義請求地址和參數
const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${???}&secret=${???}`;
// 導入發送請求的庫 request-promise-native
// request 無需導入
// npm install request request-promise-native 發送請求(服務器端不能用 ajax)
// 請求方式,請求地址,若是響應回來的數據是JSON,則自動轉化爲 js 對象
// {"access_token":"ACCESS_TOKEN", "expires_in":7200}
const access_token= await rp({method:'GET', url, json: true});
// 重寫過時時間,提早 5 分鐘刷新
accessToken
return accessToken;
} // 返回一個 Promise 對象,其中有 access_token 對象
saveAccessToken(accessToken){ // 爲了避免被修改,使用 fs 模塊將 access_token 寫入 txt 文件
// 寫入文件時,沒法寫 數組、函數、對象 類型的數據 [object Object]
new Promise((resolve, reject)=>{
writeFile('./access_token.txt', JSON.stringify(accessToken), err=>{
if(err){
reject(err);
}else{
resolve('保存 access_token 成功');
}
});
});
}
readAccessToken(){
new Promise((resolve, reject)=>{
readFile('./access_token.txt', (err, data)=>{
if(err){
reject(err);
}else{
resolve('讀取 access_token 成功');
}
});
});
}
isValidAccessToken({expires_in}){
return (expires_in > Date.now);
}
}
// 直接測試:
(async ()=>{
const w = new WeChat();
w.getAccessToken().then(async result=>{
if(w.isValidAccessToken(result)){
return result;
}else{
result = await w.getAccessToken();
await w.saveAccessToken();
return result;
};
}).catch(err=>{ // 第一次讀取,會失敗
result = await w.getAccessToken();
await w.saveAccessToken();
return result;
}).then(result=>{
console.log(result);
});
})();
接口編程(方法須要參數 access_token)
自定義菜單最多包括 3 個一級菜單
每一個一級菜單最多包含 5 個二級菜單
菜單有 10 中類型
凡是 POST 請求都有 請求體數據
只要沒有 請求體 數據,就必定是 GET 請求
const body = {
"button":[
{
"type":"click",
"name":"一級菜單微信表情",
"key":"click"
},
{
"name":"二級菜單",
"sub_button":[
{
"type":"view",
"name":"百度",
"url":"http://www.baidu.com/"
},
{
"type":"miniprogram",
"name":"wxa",
"url":"http://mp.weixin.qq.com",
"appid":"wx286b93c14bbf93aa",
"pagepath":"pages/lunar/index"
},
{
"type":"click",
"name":"贊一下咱們",
"key":"V1001_GOOD"
}]
}]
"button":[
{
"type":"click",
"name":"一級菜單微信表情",
"key":"click"
},
{
"name":"二級菜單",
"sub_button":[
{
"type":"view",
"name":"百度",
"url":"http://www.baidu.com/"
},
{
"type":"miniprogram",
"name":"wxa",
"url":"http://mp.weixin.qq.com",
"appid":"wx286b93c14bbf93aa",
"pagepath":"pages/lunar/index"
},
{
"type":"click",
"name":"贊一下咱們",
"key":"V1001_GOOD"
}]
}]
};
creaetMenu(body){
// 獲取到 access_token
const {access_token} = await this.fetchAccessToken();
// 2. 定義請求體地址
const url = `https://api.weixin.qq.com/cgi-bin/menu/create?access_token=${access_token}`;
// 3. 發送請求
const result = await rp({method:'POST', url, json:true, body});
return result;
}
源代碼:
index.js
const express = require('express'); const {interfaceInit} = require('./interfaceInit'); const app = express(); interfaceInit(); // 中控服務器 初始化 app.listen( 3000, err=>console.log(err?err:'\n\n服務器已啓動\n\t\tHunting Happy!') );
interfaceInit/index.js
/**** * access_token 對象____中控服務器----公衆號的全局惟一接口調用憑據 * * { * access_token: '17_Nq3M5HMdnX3Xwkbi48uPEaVZ4qnh_H5B8HOzBy-DnXqLz6s9h3ALAPd6sk11K0zclzu0Ap3cZciBVp2aml9EuJGmSZ-iGKe7gFOwVUEYGhOB70Il9GeCMWtppgpXcdMzm7YaqVE_W55L1bgfBEQcAHAGJV', * expires_in: 7200 * } ****/ const promiseRequest = require('request-promise-native'); const {APPID, APPSECRET, accessToken} = require('../config'); const {writeFile, readFile} = require('fs'); const {menu, deleteMenu, createMenu} = require('./menu'); class WeChat{ getValidAccessToken(){ if(this.access_token && this.isValidAccessToken(this)){ return Promise.resolve({ access_token: this.access_token, expires_in: this.expires_in }); }else{ return this.readAccessToken().then(async objAccessToken=>{ if (this.isValidAccessToken(objAccessToken)){ return objAccessToken; }else{ const newObjAccessToken = await this.requestAccessToken(); await this.saveAccessToken(newObjAccessToken); return newObjAccessToken; } }).catch(async err=>{ const newObjAccessToken = await this.requestAccessToken(); await this.saveAccessToken(newObjAccessToken); return newObjAccessToken; }).then(objAccessToken=>{ // 更新 WeChat this.access_token = objAccessToken.access_token; this.expires_in = objAccessToken.expires_in; // 返回 Promise 的 access_token return Promise.resolve(objAccessToken); }); }; } readAccessToken(){ // 1、讀取access_token的方法 return new Promise((resolve, reject)=>{ readFile('./access_token.txt', (err, buffer)=>{ if(err){ reject('Read ./access_token.txt' + err); }else{ resolve(JSON.parse(buffer.toString())); } }); }); } isValidAccessToken({expires_in}){ // 2、判斷 access_token 是可用的嗎? return expires_in > Date.now(); }; async requestAccessToken(){ // 3、發送請求 getAccessToken() 獲取 access_token // 1. access_token 請求 url const url = `${accessToken}appid=${APPID}&secret=${APPSECRET}`; // 2. POST 請求 access_token 對象 const objAccessToken = await promiseRequest({ method: 'POST', url, json: true }); // 重寫過時時間,提早 5 分鐘刷新 objAccessToken.expires_in = Date.now() - (7200 - 300)*1000; return objAccessToken; } saveAccessToken(objAccessToken){ // 4、保存 access_token 到文件 return new Promise((resolve, reject)=>{ // 異步執行文件寫完 writeFile('./access_token.txt', JSON.stringify(objAccessToken), err=>{ if(err){ reject("Write Success."); }else{ resolve('access_token 最新已保存'); }; }); }); } } module.exports = { async interfaceInit(){ const wechat = new WeChat(); console.log('---- 先刪除菜單 ----'); const deleteRet = await deleteMenu(wechat); console.log(deleteRet); console.log('---- 再建立菜單 ----'); const createRet = await createMenu(wechat, menu); console.log(createRet); } };
interfaceInit/menu.js
const {menuDelete, menuCreate} = require('../config'); const promiseRequest = require('request-promise-native'); module.exports = { async deleteMenu(wechat){ const {access_token} = await wechat.getValidAccessToken(); const url = `${menuDelete}access_token=${access_token}`; return await promiseRequest({method: 'Get', url, json: true}); }, async createMenu(wechat, menu){ const {access_token} = await wechat.getValidAccessToken(); const url = `${menuCreate}access_token=${access_token}`; return await promiseRequest({method: 'POST', url, json: true, body: menu}); }, menu: { "button":[ { "type":"click", "name":"一級菜單☀", "key":"click" }, { "name":"二級菜單⛄", "sub_button":[ { "type":"view", "name":"百度🌕", "url":"http://www.atguigu.com/" }, { "type": "scancode_waitmsg", "name": "掃碼帶提示🌸", "key": "rselfmenu_0_0", }, { "type": "scancode_push", "name": "掃碼推事件", "key": "rselfmenu_0_1", }, { "type": "pic_sysphoto", "name": "系統拍照發圖", "key": "rselfmenu_1_0", "sub_button": [ ] }, { "type": "pic_photo_or_album", "name": "拍照或者相冊發圖", "key": "rselfmenu_1_1", "sub_button": [ ] }, ] }, { "name":"二級菜單", "sub_button":[ { "type": "pic_weixin", "name": "微信相冊發圖", "key": "rselfmenu_1_2" }, { "name": "發送位置", "type": "location_select", "key": "rselfmenu_2_0" }, // { // "type": "media_id", // "name": "圖片", // "media_id": "MEDIA_ID1" // }, // { // "type": "view_limited", // "name": "圖文消息", // "media_id": "MEDIA_ID2" // } ] } ] } };
config/index.js
const prefix = 'https://api.weixin.qq.com/cgi-bin/'; module.exports = { token: 'FinnKou', APPID: 'wxba5329dbd7d2asd2cd32d', APPSECRET: '62ad75995d342f27668120fcb618d77b2e31', accessToken: `${prefix}token?grant_type=client_credential&`, menuDelete: `${prefix}menu/delete?`, menuCreate: `${prefix}menu/create?` };