微信公衆号項目介紹
目前隻對實作思路進行說明,不作基礎介紹!詳細資訊檢視微信官方文檔及git位址!
實作功能
- 新關注自動回複;
- 被動回複;
- 推送消息;
- 自定義菜單;
- js-sdk使用;
- 網頁授權擷取使用者資訊
koa2環境搭建
- koa-generator的安裝
cnpm install -g koa-generator
- koa2項目建立
koa2 -e wechat
使用飛鴿内網穿透
飛鴿
使用比較簡單就不作詳細介紹了!
接入微信公衆平台開發
編寫reply中間件;建立reply檔案夾,進入檔案夾建立index檔案!
安裝所需依賴:
cnpm i -S sha1
在app.js入口檔案中路由配置前引入中間件!
...
const reply = require("./reply")
...
//接收處理所有消息
app.use(reply());
// routes
app.use(index.routes(), index.allowedMethods())
...
驗證消息的确來自微信伺服器
微信伺服器會發送兩種類型的消息給開發者伺服器.
開發者通過檢驗signature對請求進行校驗(下面有校驗方式)。若确認此次GET請求來自微信伺服器,請原樣傳回echostr參數内容,則接入生效,成為開發者成功,否則接入失敗。
-
GET請求
-驗證伺服器的有效性
1)将token、timestamp、nonce三個參數進行字典序排序
2)将三個參數字元串拼接成一個字元串進行sha1加密
3)開發者獲得加密後的字元串可與signature對比,辨別該請求來源于微信
-
POST請求
-微信伺服器會将使用者發送的資料以post請求的方式轉發到開發者伺服器上
const config = require("../config/config")
const sha1 = require("sha1");
module.exports = () => {
return async(ctx, next) => {
const { signature, timestamp, nonce, echostr } = ctx.query;
const token = config.wechat.token;
let str = [token, timestamp, nonce].sort().join('');
const sha = sha1(str);
if (ctx.method === "GET" && sha === signature) {
//如果一樣說明消息來自于微信伺服器,傳回echostr給微信伺服器
ctx.body = echostr;
} else if (ctx.method === "POST" && sha === signature) {
} else {
await next();
// ctx.body = "Failed"
}
}
}
在測試号中進行配置,若無誤會提示配置成功!
360截圖167401157264113_看圖王.png
繼續處理post請求,并實作回複消息
安裝依賴raw-body和xml2js:
cnpm i -S raw-body xml2js
處理POST請求,具體實作方法移步源碼檢視!
...
const getRawBody = require("raw-body");
const { parseXML, formatMessage, tpl2xml } = require("../utils/tool")
const reply = require("./reply")
...
const data = await getRawBody(ctx.req, {
length: ctx.length,
limit: "1mb",
encoding: ctx.charset
})
const content = await parseXML(data);
// console.log(content);
const message = formatMessage(content.xml);
// console.log(message);
let replyBody = await reply(message);
// console.log(replyBody);
//生成xml資料
let xml = tpl2xml(replyBody, message);
// console.log(xml)
ctx.status = 200;
ctx.type = 'application/xml';
ctx.body = xml;
...
reply.js
/**
* 處理使用者發送的消息和内容,傳回不同的内容給使用者
*/
module.exports = async(message) => {
let replyBody = "您在說什麼,我聽不懂!";
if (message.MsgType === "text") {
let content = message.Content;
if (content === "熱門") {
replyBody = "熱門";
} else if (content === "2") {
replyBody = "落地成盒";
} else if (content.match("愛")) {
replyBody = "我愛你~";
}
} else if (message.MsgType === "voice") {
options.msgType = "voice";
options.mediaId = message.MediaId;
console.log(message.Recognition);
} else if (message.MsgType === "event") {
if (message.Event === "subscribe") {
replyBody = "歡迎您的關注~\n" +
"回複 首頁 檢視電影預告片頁面\n" +
"回複 熱門 檢視最新熱門電影\n" +
"回複 文本 檢視指定電影資訊\n" +
"回複 語音 檢視指定電影資訊\n" +
"也可以點選下面的菜單按鈕來了解其它資訊~";
} else if (message.Event === "unsubscribe") {
console.log("使用者取消關注了!")
} else if (message.Event === "CLICK") {
replyBody = "您可以按照以下提示來進行操作~\n" +
"回複 首頁 檢視電影預告片頁面\n" +
"回複 熱門 檢視最新熱門電影\n" +
"回複 文本 檢視指定電影資訊\n" +
"回複 語音 檢視指定電影資訊\n" +
"也可以點選下面的菜單按鈕來了解其它資訊~";
}
}
return replyBody;
}
自定義菜單及微信JS-SDK分享接口執行個體
根目錄建立wechat檔案夾,進入檔案夾建立menu.js和wechat.js檔案。
wechat.js封裝了access_token、jsapi_ticket、建立和删除菜單!
自定義菜單
/*routes/index.js*/
...
// 建立執行個體對象
const Wechat = require("../wechat/wechat")
const wechatApi = new Wechat();
//menu.js檔案重新配置菜單
router.get('/updateMenu', async(ctx, next) => {
let result = await wechatApi.createMenu(menu);
ctx.body = result
})
...
JSSDK使用步驟
-
步驟一:綁定域名
先登入微信公衆平台進入“公衆号設定”的“功能設定”裡填寫“JS接口安全域名”。
備注:登入後可在“開發者中心”檢視對應的接口權限。
-
步驟二:引入JS檔案
在需要調用JS接口的頁面引入如下JS檔案,(支援https):http://res.wx.qq.com/open/js/jweixin-1.6.0.js
如需進一步提升服務穩定性,當上述資源不可通路時,可改通路:http://res2.wx.qq.com/open/js/jweixin-1.6.0.js (支援https)。
備注:支援使用 AMD/CMD 标準子產品加載方法加載
-
步驟三:通過config接口注入權限驗證配置
所有需要使用JS-SDK的頁面必須先注入配置資訊,否則将無法調用(同一個url僅需調用一次,對于變化url的SPA的web app可在每次url變化時進行調用,目前Android微信用戶端不支援pushState的H5新特性,是以使用pushState來實作web app的頁面會導緻簽名失敗,此問題會在Android6.2中修複)。
wx.config({ debug: true, // 開啟調試模式,調用的所有api的傳回值會在用戶端alert出來,若要檢視傳入的參數,可以在pc端打開,參數資訊會通過log打出,僅在pc端時才會列印。 appId: '', // 必填,公衆号的唯一辨別 timestamp: , // 必填,生成簽名的時間戳 nonceStr: '', // 必填,生成簽名的随機串 signature: '',// 必填,簽名 jsApiList: [] // 必填,需要使用的JS接口清單 });
- 步驟四:通過ready接口處理成功驗證
wx.ready(function(){ // config資訊驗證後會執行ready方法,所有接口調用都必須在config接口獲得結果之後,config是一個用戶端的異步操作,是以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確定正确執行。對于使用者觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。 });
- 步驟五:通過error接口處理失敗驗證
wx.error(function(res){ // config資訊驗證失敗會執行error函數,如簽名過期導緻驗證失敗,具體錯誤資訊可以打開config的debug模式檢視,也可以在傳回的res參數中檢視,對于SPA可以在這裡更新簽名。 });
建立jssdk路由權限簽名
/*routes/index.js*/
...
const { appID } = require("../config/config").wechat;
...
//用于JS-SDK使用權限簽名算法
router.get('/jssdk', async(ctx, next) => {
/* JS-SDK使用權限(簽名算法)
簽名生成規則如下:參與簽名的字段包括noncestr(随機字元串),
有效的jsapi_ticket, timestamp(時間戳), url(目前網頁的URL,不包含#及其後面部分) 。
*/
//擷取傳入的url
let url = ctx.query.url;
const { ticket } = await wechatApi.fetchTicket();
const nonceStr = Math.random().toString().split(".")[1];
const timestamp = Date.now();
const arr = [`jsapi_ticket=${ticket}`, `noncestr=${nonceStr}`, `timestamp=${timestamp}`, `url=${url}`];
const str = arr.sort().join("&");
const signature = sha1(str);
ctx.body = {
appId: appID,
signature,
nonceStr,
timestamp
}
})
...
前端使用
<!-- /*public/test.html*/ -->
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://libs.cdnjs.net/zepto/1.2.0/zepto.min.js"></script>
<script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
<body>
<div>123</div>
<script>
$(function() {
let shareUrl = location.href.split('#')[0];
$.ajax({
url: "http://caorui.max.svipss.top/jssdk",
data: {
url: shareUrl
},
success: function(data) {
wx.config({
//debug: true, // 開啟調試模式,調用的所有api的傳回值會在用戶端alert出來,若要檢視傳入的參數,可以在pc端打開,參數資訊會通過log打出,僅在pc端時才會列印。
appId: data.appId, // 必填,公衆号的唯一辨別
timestamp: data.timestamp, // 必填,生成簽名的時間戳
nonceStr: data.nonceStr, // 必填,生成簽名的随機串
signature: data.signature, // 必填,簽名
jsApiList: [ // 必填,需要使用的JS接口清單
"updateAppMessageShareData",
"updateTimelineShareData"
]
});
wx.ready(function() {
wx.updateAppMessageShareData({
title: '分享測試12dsd', // 分享标題
desc: '分享描述cgngn', // 分享描述
link: shareUrl, // 分享連結
imgUrl: '分享圖示', // 分享圖示
success: function() {
// 使用者确認分享後執行的回調函數
}
});
wx.updateTimelineShareData({
title: '分享測試12fsf', // 分享标題
link: shareUrl, // 分享連結
imgUrl: '分享圖示', // 分享圖示
success: function() {
// 使用者确認分享後執行的回調函數
}
});
wx.error(function(res) {
alert(res.errMsg); // 正式環境記得關閉啊!!!!
});
})
}
})
})
</script>
</body>
</html>
網頁授權擷取使用者資訊
微信官方文檔
具體而言,網頁授權流程分為四步:
1、引導使用者進入授權頁面同意授權,擷取code
2、通過code換取網頁授權access_token(與基礎支援中的access_token不同)
3、如果需要,開發者可以重新整理網頁授權access_token,避免過期
4、通過網頁授權access_token和openid擷取使用者基本資訊(支援UnionID機制)
在routes/index.js添加路由
//微信網頁授權擷取code
router.get("/oauth", async(ctx, next) => {
let redirect_uri = `http%3a%2f%2fcaorui.max.svipss.top/oauth.html`;
ctx.redirect(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appID}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect`)
})
//擷取授權後的使用者資訊
router.get("/getUserInfo", async(ctx, next) => {
//擷取code值
let code = ctx.query.code;
if (!code) {
ctx.redirect('http://caorui.max.svipss.top/oauth')
}
let result = await wechatApi.getOauthAccessToken(code);
let data = await wechatApi.getOauthUserinfo(result.access_token, result.openid);
ctx.body = data;
})
通過code擷取AccessToken 和 擷取授權後的使用者資料 的方法檢視wechat.js檔案!
在public/oauth.html中使用:
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://libs.cdnjs.net/zepto/1.2.0/zepto.min.js"></script>
<script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
<body>
<div>
<button id="oauth">擷取使用者資訊</button>
<div id="userInfo"></div>
</div>
<script>
$(function() {
//擷取url參數
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
return (false);
}
var userinfo = JSON.parse(localStorage.getItem("userinfo"));
// console.log(userinfo)
if (userinfo) {
$("#oauth").hide();
var html = `
<image src="${userinfo.headimgurl}"/>
<h2>你已經登入</h2>
`;
$("#userInfo").html(html);
return;
}
$("#oauth").on("click", function() {
location.href = "http://caorui.max.svipss.top/oauth";
})
var code = getQueryVariable("code");
if (code) {
$.ajax({
url: "http://caorui.max.svipss.top/getUserInfo",
data: {
code
},
success: function(data) {
$("#oauth").hide();
// console.log(data)
localStorage.setItem("userinfo", JSON.stringify(data));
var html = `
<image src="${data.headimgurl}"/>
<p>nickname:${data.nickname}</p>
<p>country:${data.country}</p>
<p>province:${data.province}</p>
<p>city:${data.city}</p>
<p>openid:${data.openid}</p>
`
$("#userInfo").html(html)
}
})
}
})
</script>
</body>
</html>
結果如下:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CNzkTOhRGZhN2Y5czN2UGN1QjMhBDMzImYwczN2EDNh9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
轉自https://www.jianshu.com/p/2006cf85b1cf