這兩個月里,公司的項(xiàng)目基本上都基于微信公眾平臺的開發(fā)上。為了可以快速開發(fā),我使用了 Ruby on Rails框架,而不是之前的 Python,這不是說 Python 不可以做到快速開發(fā),其實(shí)我更多只是想試試新的東西。但效果不錯。
微信公眾平臺的開發(fā),與語言沒什么太大的關(guān)系,大家只要選擇自己熟悉的語言即可,以下我只說明大概的原理,并用 Ruby on Rails 作為代碼示例。
準(zhǔn)備工作
首先,你需要有一個微信公眾號,比如 ”SheldonChen寫字的地方"。在往下繼續(xù)閱讀前,請自覺掏出手機(jī),打開微信掃一掃:

其次,你需要有一個獨(dú)立域名的網(wǎng)站,用來和微信服務(wù)器交互。
接入公眾平臺
登錄微信公眾平臺后臺后,點(diǎn)“功能”-“高級功能”-“開發(fā)模式”,進(jìn)入開發(fā)模式,如果公眾平臺顯示“尚未成為
開發(fā)者”,就點(diǎn)擊“成為開發(fā)者”:

同意協(xié)議后,填寫URL和Token:

URL是指微信服務(wù)器向哪個URL發(fā)送消息,假設(shè)我們自己的服務(wù)器域名是www.example.com,準(zhǔn)備用/weixin來接收消息,就填寫:
http://www.example.com/weixin
而Token是微信服務(wù)器和我們自己的服務(wù)器通信時驗(yàn)證身份用的,可以隨便填寫,但要注意保密。
然后點(diǎn)“提交”,一般來說會報錯“URL超時”或者“沒有正確返回echostr”,因?yàn)槲覀兊暮笈_還沒有準(zhǔn)備好,所以,第一步是接收微信后臺發(fā)送的驗(yàn)證消息,微信后臺會發(fā)送一個GET請求到上面的URL,并附帶以下參數(shù):
signature,timestamp,nonce,echostr
我們的服務(wù)器在接收到上述參數(shù)后,需要驗(yàn)證signature是否正確,驗(yàn)證方法是先對timestamp、nonce和token先排序,再拼接成一個字符串,計算出sha1,并和signature對比。
注意token不是微信服務(wù)器發(fā)過來的,而是我們自己寫死的一個常量,就是在微信后臺填寫的Token。
如果計算的sha1和微信傳過來的signature相等,說明這個請求確實(shí)是微信后臺發(fā)過來的,如果是別人偽造的請求,由于他不知道token,所以,無法計算出正確的signature。
要防止第三方通過監(jiān)聽發(fā)動replay攻擊,還需要驗(yàn)證timestamp和nonce,這個以后再討論。
如果signature計算無誤,就把微信后臺傳過來的echostr原封不動地傳回去,這樣,就可以通過驗(yàn)證,成為開發(fā)者。
在確保開發(fā)模式打開的情況下,微信后臺會把用戶消息發(fā)到我們的服務(wù)器上,也就是URL:http://www.example.com/weixin:

微信后臺發(fā)送消息是一個POST請求,但和普通的POST請求不同的是,首先,URL會帶上signature、timestamp、nonce這3個參數(shù):
POST http://www.example.com/weixin?signature=xxx×tamp=123456&nonce=123
然后,HTTP請求的BODY是一個不規(guī)范的XML:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
我們自己的服務(wù)器只需要處理該XML,然后,向微信返回一個類似如下的XML:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
就可以完成消息的回復(fù)。微信后臺要求必須在5秒內(nèi)回復(fù),最多重試3次,否則我們自己的回復(fù)消息就到達(dá)不了用戶的手機(jī)了。如果我們自己的服務(wù)器無法在5秒內(nèi)回復(fù),就回復(fù)一個空字符串,告訴微信服務(wù)器,不用重試了,這個消息處理不了,不給用戶回復(fù)了。
上面的交互邏輯看起來很簡單,但實(shí)際上坑有很多。
首先,微信服務(wù)器發(fā)送的POST請求根本就不符合HTTP規(guī)范。原則上POST請求不應(yīng)該在URL上附帶參數(shù),但微信后臺偏偏要這么干,這就讓很多編程語言的標(biāo)準(zhǔn)框架無法獲取到POST參數(shù),因?yàn)闃?biāo)準(zhǔn)的POST參數(shù)是從HTTP BODY中解析的。
所以,從POST獲取URL參數(shù)就需要用到更底層的代碼。