Part 1. 驗(yàn)證公眾號(hào) (開(kāi)發(fā)者)
- 名詞解釋
- timestamp 時(shí)間戳
- nonce 隨機(jī)數(shù)
- token+timestamp+nonce
- signature 簽名
- 驗(yàn)證順序
- 上面三者排序
- 拼接后利用sha1模塊進(jìn)行加密
- 與signature進(jìn)行對(duì)比
相等就表示來(lái)源于微信,返回echostr
Part 2. acess_token獲取與儲(chǔ)存
- 當(dāng)普通微信用戶向公眾賬號(hào)發(fā)消息時(shí)哗总,微信服務(wù)器將POST消息的XML數(shù)據(jù)包到開(kāi)發(fā)者填寫(xiě)的URL上。
- 名詞解釋
- acess_token :憑據(jù)笼踩,有效期2h,沒(méi)兩個(gè)小時(shí)就要重新獲戎粘椤(獲取完上一個(gè)失效)一種憑證戳表,而且要保證他只在唯一位置,不然會(huì)發(fā)生沖突官方解釋
- 設(shè)計(jì)思路
- 創(chuàng)建構(gòu)造函數(shù)昼伴,有請(qǐng)求方的appID匾旭、appSecret屬性以及對(duì)acess_token進(jìn)行操作的兩個(gè)方法getAccessToken、saveAccessToken
- 在其原型上還定義了兩個(gè)方法isVaildAccessToken圃郊、updateAccessToken价涝,分別用來(lái)識(shí)別憑證的有效性、若失效或者沒(méi)有憑證則重新發(fā)送請(qǐng)求進(jìn)行acess_token更新
- 創(chuàng)建了一個(gè)txt文件來(lái)儲(chǔ)存acess_token(使用了fs模塊:讀取寫(xiě)入文件)持舆,文件寫(xiě)入和讀取功能可以單獨(dú)儲(chǔ)存在一個(gè)公共方法util.js中色瘩,配置文檔路徑(path模塊)
Part 3. 回復(fù)測(cè)試
- 流程
- 接收請(qǐng)求以及對(duì)請(qǐng)求信息的處理
接收的請(qǐng)求使用getRawBody模塊來(lái)進(jìn)行處理,可以拼裝成一個(gè)bufferde的xml數(shù)據(jù)逸寓,其參數(shù)是請(qǐng)求對(duì)象居兆,對(duì)應(yīng)也就是ctx.req。
因?yàn)楂@取的是一個(gè)xml數(shù)據(jù)竹伸,要對(duì)xml轉(zhuǎn)化成js泥栖,使用了xml2js模塊簇宽,然后獲取的xml對(duì)象中是數(shù)組形式,要重新遍歷吧享,將其元素剖析出來(lái)魏割,所以編寫(xiě)了一個(gè)formatMessage的方法,分別判斷屬性值是否存在或類型是否為對(duì)象钢颂,然后遍歷到新的對(duì)象钞它,這個(gè)方法有點(diǎn)像寫(xiě)原生對(duì)象深復(fù)制,最后返回一個(gè)完整的對(duì)象數(shù)據(jù)message殊鞭。
- 返回?cái)?shù)據(jù)到用戶
- 判斷請(qǐng)求方法是'GET'還是'POST'遭垛,當(dāng)請(qǐng)求方法也就是message.MsgType為‘GET’,我們可以通過(guò)判斷message.Event類型操灿,來(lái)響應(yīng)用戶端的具體操作
- e.g(subscribe訂閱事件) 當(dāng)message.Event 為 'subscribe' 是用戶進(jìn)行了訂閱操作耻卡,我們通過(guò)書(shū)寫(xiě)響應(yīng)體來(lái)進(jìn)行該請(qǐng)求的響應(yīng),返回的數(shù)據(jù)類型是ctx.type = 'application/xml'牲尺,以及返回成功狀態(tài) ctx.status = 200,還有響應(yīng)的內(nèi)容要依據(jù)官方給定文檔格式返回幌蚊,ctx.body = 官方特定格式
- e.g(返回文本消息)其格式為
ctx.body = '<xml>' +
'<ToUserName><![CDATA[' + message.FromUserName + ']]></ToUserName>' +
'<FromUserName><![CDATA[' + message.ToUserName +']]></FromUserName>' +
'<CreateTime>' + now + '</CreateTime>' +
'<MsgType><![CDATA[text]]></MsgType>' +
'<Content><![CDATA[歡迎關(guān)注Chan的微信公眾號(hào)]]></Content>' +
'</xml>'
// FromUserName 接收方
// ToUserName 發(fā)送方
// CreateTime 時(shí)間戳
// MsgType 消息類型
// Content 消息數(shù)據(jù)
Part 4. 消息處理
- 模板書(shū)寫(xiě)
模板書(shū)寫(xiě)的是返回格式谤碳,返回的格式是微信定好的,可以在官方文檔中查詢溢豆,主要區(qū)別是在回復(fù)類型的不同蜒简,回復(fù)的格式也不同。通過(guò)變量msgType(返回?cái)?shù)據(jù)類型)來(lái)編譯模板進(jìn)行輸出漩仙,因?yàn)榉祷氐氖亲址唇哟瓴纾允褂昧薶eredoc模塊(書(shū)寫(xiě))以及ejs模塊(編譯)來(lái)對(duì)應(yīng)數(shù)據(jù)類型的輸出 - 定制業(yè)務(wù)回復(fù)數(shù)據(jù)
回復(fù)格式的模板寫(xiě)好了,我們可以定義回復(fù)內(nèi)容队他,并返回回復(fù)內(nèi)容等待下一步輸出卷仑,依據(jù)回復(fù)格式,回復(fù)文本則只需要設(shè)置content變量麸折,而視頻或文章等需要定義多個(gè)變量锡凝,所以創(chuàng)建一個(gè)新的模塊來(lái)定制回復(fù)內(nèi)容的業(yè)務(wù)處理。- 因?yàn)槭褂胟oa所以該步驟通過(guò)await調(diào)用并獲取微信服務(wù)器返回并處理過(guò)的message數(shù)據(jù)垢啼。依據(jù)用戶提供的信息設(shè)置回復(fù)內(nèi)容content窜锯,最終返回需要回復(fù)的content,并調(diào)用next芭析,執(zhí)行下一步代碼
- 此時(shí)app已經(jīng)獲得了content锚扎,需要通過(guò)第一步制作的模板編譯后保存到ctx.body,然后設(shè)置status馁启, type驾孔。對(duì)微信服務(wù)器發(fā)送請(qǐng)求,等待服務(wù)器對(duì)用戶進(jìn)行信息回復(fù)。
Part 5. 微信SDK應(yīng)用
- 配置
- 綁定域名 (備案助币,一級(jí)以上)
- 引入JS文件 (直接添加一個(gè)html頁(yè)面到一個(gè)中間件中:可以不用路由 )
- JS SDK的初始化 -- 使得內(nèi)置網(wǎng)頁(yè)可以調(diào)用微信原生應(yīng)用(掃一掃,搖一搖眉菱,拍照迹栓,語(yǔ)音等)
依賴
koa 框架 類似express框架 --回調(diào)中間件應(yīng)用
sha1 --加密模塊
request --請(qǐng)求封裝模塊 --請(qǐng)求使用
bluebird --node上的實(shí)現(xiàn)Pormise的模塊,而且還可以讓request模塊promisify化
raw-body --解析請(qǐng)求還能限制其數(shù)據(jù)內(nèi)容或格式
xml2js --將xml數(shù)據(jù)格式轉(zhuǎn)化成js對(duì)象
heredoc --創(chuàng)建多行字符串
ejs --可以在編譯模板中使用了<% %>的代碼俭缓,使得在字符創(chuàng)中可以直接使用JS代碼
ES6知識(shí)須知
- generator 生成器(可以使得函數(shù)在哪一步進(jìn)行暫屠绻颍或繼續(xù)執(zhí)行等) generator,配合yield方法來(lái)確定回調(diào)順序
- Promise ----> 使得回調(diào)更好的維護(hù)迁杨,更清晰凄硼,將回調(diào)的函數(shù)單獨(dú)抽離出來(lái)分別用
.then(成功執(zhí)行)
.catch(錯(cuò)誤執(zhí)行)
node blurbird模塊實(shí)現(xiàn)了原生promise的方法
new Promise((resolve, reject) => {}) // 使用方法
成功使用resolve返回,失敗使用reject返回
問(wèn)題
- koa運(yùn)行時(shí)警告
koa deprecated Support for generators will be removed in v3
使用新的寫(xiě)法即可婉刀,參考
https://github.com/koajs/koa/
注意使用了koa2以上的版本后響應(yīng)和請(qǐng)求對(duì)象不再this突颊,而是在一開(kāi)始設(shè)置的ctx參數(shù)中了。之前一般this獲取的方法或?qū)傩垣@取不到注意下是不是ctx有關(guān)。 - request發(fā)送請(qǐng)求后返回的data在響應(yīng)對(duì)象responce的body屬性中
.then(res=>{console.log(res.body)}) ===>request promisify化 - 當(dāng)和微信公眾號(hào)平臺(tái)進(jìn)行連接時(shí)糙申,提示了token check fail 但是返回的數(shù)據(jù)又相等柜裸,可能是域名填寫(xiě)的問(wèn)題(我是用的是云服務(wù)器給的公網(wǎng)IP解析了一個(gè)域名缕陕,域名沒(méi)備案不讓使用,所以能接受但用不了疙挺,可以直接用IP連接)
- Koa中間件的數(shù)據(jù)傳遞扛邑,可以將數(shù)據(jù)儲(chǔ)存在ctx.state,亦或是使用call方法將執(zhí)行上下文改為ctx铐然,來(lái)獲取數(shù)據(jù)蔬崩。
// app.js
ctx.state.msg = message // 把解析好的message儲(chǔ)存 koa的特有方法就是存在state中
await handler(ctx, next) // 讓業(yè)務(wù)處理儲(chǔ)存好的message
// handler.js
var message = ctx.state.msg // 來(lái)獲取儲(chǔ)存的數(shù)據(jù)
reply.call(ctx) // 處理完后調(diào)用回復(fù)的方法
// reply.js
var body = this.body // this.body ,因?yàn)榻壎薱tx ===> 和在app中調(diào)用 ctx.body一樣
- 在上傳臨時(shí)素材并設(shè)置公眾號(hào)返回信息時(shí),若文件比較大上傳時(shí)間長(zhǎng)搀暑,微信服務(wù)器等待響應(yīng)超過(guò)5s則不會(huì)返回視頻舱殿,并且會(huì)重復(fù)3次請(qǐng)求,這樣就進(jìn)行了3次上傳险掀,一般情況下不建議自動(dòng)回復(fù)臨時(shí)上傳的視頻,而是直接調(diào)用上傳后的素材后微信服務(wù)器返回的media_id
- 有時(shí)候會(huì)提示.then 未定義湾宙,是因?yàn)榍懊娴拇a有的沒(méi)有返回Promise對(duì)象樟氢,多以導(dǎo)致使用不了then的方法。
- 有關(guān)相機(jī)相冊(cè)侠鳄、地理位置的信息事件信息埠啃,微信服務(wù)器會(huì)先返回一個(gè)當(dāng)前事件的事件信息,然后在返回一個(gè)普通消息(照片對(duì)應(yīng)回復(fù)照片信息伟恶,地理位置對(duì)應(yīng)地理位置)掃描二維碼只返回一個(gè)事件消息碴开。
- koa中中間件傳遞POST數(shù)據(jù)時(shí)記得next(ctx),要將ctx傳遞下去否則會(huì)發(fā)生FORM數(shù)據(jù)錯(cuò)誤