html
以前寫了一篇搭建sae的python平臺,經過微信公衆平臺驗證的博文。如今就是要承上啓下,開始開發的第一步。也許你會說開發的第一步就是寫代碼。那我會對你表示鄙視,儘管南苑隨筆是個很簡單的應用,可是多少也還須要設計一下。肯定一下需求,爲了未來可以吸引到粉絲來關注個人公衆號,我必需要定位好個人app,而後提供優秀的內容,並肯定它的運營模式,才能進一步地提高咱們的應用水準。python
首先,這個app是要用python寫的,那麼就必定要優雅一點,pythonic一點。對於我本身來講,我要使得這個app至少可以知足我閱讀若干與南苑相關的文章的要求才行。故而須要對南苑隨筆進行需求分析:linux
定位: 爲南苑或者外校同窗提供咱們在南苑生活和學習的文章。文體不限,內容形式包括{圖片,語音,文字},推送給用戶,這正是微信可以提供給個人。web
約定: 容許投稿,可是投稿必須經過管理員的審覈。其實很大程度上,咱們是爲了向粉絲們推介 @bibo-果凍 和 @daoluan-鄭思願 合做的那本《南苑隨筆——IT小小鳥外傳》。數據庫
內容: 校園生活,學習,感情,分享,趣味,攝影,文學做品,簡單社交 ,歌曲等等。django
服務: 提供每1天/2天按期推送文章或語音。同時相應用戶發來的請求,對用戶請求進行分析後返回所要求的內容。編程
性能: 要求服務器在5秒內返回用戶的請求內容,在網絡差的時候,這個就難說了。可以同時處理至少20位粉絲的請求(其實到底有沒有這麼多粉絲髮來請求,我內心仍是沒有數啊)。ubuntu
此次,我吸收了上次的經驗教訓,要先完成邏輯和數據的設計,最後纔去設計咱們的交互界面(包括交互方式和菜單)。數據先行,那麼一切就從構建起數據庫開始。參照wordpress的博客數據庫,就能夠設計出基本的數據庫框架。這部分就交給 @daoluan-思願 來搞定,我相信他會完成的至關出色。瀏覽器
本系統須要一個博客系統,和一個微信服務app,博客系統做爲web端,用瀏覽器可訪問的。而微信app則負責推送到用戶的微信賬號聊天界面。故而咱們的數據庫設計就很清晰了,須要:服務器
user表——用戶
post表——文章
blog配置——博客的設置
category表——分類
———————————次要———————————
url表——友情連接
guest_book表——留言簿
而我須要用到的僅僅是博客系統數據庫的一個子集——post表和guest_book表(也許會用到category表)。故而開發微信app則只須要自行建立兩個表進行實驗便可。
暫且不把設計邏輯排到日程上來,咱們來嘗試一下在第三方服務器和微信服務器,以及客戶端進行通訊。假如用戶發送來一個請求,因爲已經綁定了URL,且開啓了開發模式,那麼就微信服務器就會轉發這個請求到咱們的服務器,咱們的服務器要解析這個請求,而後返回所請求的內容。
咱們須要一個類來處理get請求,以及定時向微信服務器傳送羣發的文章。
該類 weixin_scnuwriter設計以下:
handle_request(request) # 處理請求
parse_request_xml(xml) # 解析請求
response_msg(request) # 響應請求(關鍵邏輯部分)
pack_text_xml(post_msg, response_msg) # 打包響應的文本內容,須要參考微信API,供response_msg調用
# 另外在數據庫方面要可以讀取,須要django於數據庫打交道,不過咱們暫時不實現,只是作一個簡單的測試。
下面是handle_request代碼:
1
2
3
4
5
6
7
8
9
|
def
handle_request(request):
if
request.method
=
=
'GET'
:
response
=
HttpResponse(check_signature(request),content_type
=
"text/plain"
)
# 若是是GET請求,那麼覈對簽名
return
response
elif
request.method
=
=
'POST'
:
response
=
HttpResponse(response_msg(request),content_type
=
"application/xml"
)
# 若是是POST請求,那麼響應消息
return
response
else
:
return
None
|
咱們以前寫認證的時候,都是直接將url導向到check_signature直接進行響應處理,可是這裏咱們對請求的類型進行區分,由於驗證的時候發送的是GET請求,而對咱們本身的服務器推送消息的時候,它是使用POST請求的,由於POST方法能夠向服務器發送大量的數據(表單提交,文件上傳等),在這裏是xml結構。記得要在urls裏面配置一個到handle_request的映射,個人是:「 url(r'^$', handle_request), 」。逗號是不能丟的。
handle_request須要調用check_signature,以前咱們用了return HttpResponse(echoStr)來驗證,可是咱們把HttpResponse的封裝放到了handle_request中,那麼咱們就只需在check_signature中返回echoStr就好了,固然,驗證一次就好了,如今已經進入開發階段,這函數將用不到。其實應該在一開始,咱們就應該像如今同樣寫。check_signature以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 檢測簽名,只對get有效
def
check_signature(request):
global
TOKEN
signature
=
request.GET.get(
"signature"
,
None
)
timestamp
=
request.GET.get(
"timestamp"
,
None
)
nonce
=
request.GET.get(
"nonce"
,
None
)
echoStr
=
request.GET.get(
"echostr"
,
None
)
token
=
TOKEN
tmpList
=
[token,timestamp,nonce]
tmpList.sort()
tmpstr
=
"%s%s%s"
%
tuple
(tmpList)
tmpstr
=
hashlib.sha1(tmpstr).hexdigest()
if
tmpstr
=
=
signature:
return
echoStr
else
:
return
None
|
很明顯15行變了。瀏覽器和服務器之間,並非簡單的傳遞數據,還有解析包的過程,服務器返回一個http類型的文檔,瀏覽器要進行協議解析,讀取頭部等東西,進而解析出整個文檔。這就是爲何須要在返回的時候包裝成HttpResponse,不然瀏覽器讀不懂,django就報錯說:返回的不是一個httpresponse類型的對象。我曾經參考《linux C 編程指南》寫過一個簡單的服務器程序,服務器程序,發送了固定的頭部,而後對各類不一樣類型的返回打印不一樣的文檔,例如404,503等等,而對於正常狀況,則讀取一個html文件予以返回。協議與就是相互商量好的約定,故而協議通常都約定了要以什麼開頭,或者按照協議裏寫的東西來解析。
下面是parse_request_xml函數,用來解析請求,該請求來自微信服務器,包含了一個xml結構,使用的是POST方法,待會咱們就用個工具來測試一下怎麼模擬POST請求,而不使用微信發送(微信發送須要通過微信服務器,並且咱們不知道錯在哪裏)。
1
2
3
4
5
6
7
|
# 解析請求,拆解到一個字典裏
def
parse_request_xml(rootElem):
msg
=
{}
if
rootElem.tag
=
=
'xml'
:
for
child
in
rootElem:
msg[child.tag]
=
smart_str(child.text)
# 得到內容
return
msg
|
很明顯,對於一個xml結構(實際上是一個字符串),進行解析,就須要用到它的tag,來區分究竟是什麼內容,以及獲取對應tag的內容,放到python咱們程序中的一個字典結構裏。
下面是response_msg函數:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def
response_msg(request):
# 從request中獲取輸入文本
rawStr
=
smart_str(request.raw_post_data)
# 將文本進行解析,獲得請求的數據
msg
=
parse_request_xml(ET.fromstring(rawStr))
# 根據請求消息來處理內容返回
query_str
=
msg.get(
"Content"
)
# query_str = "hello"
response_msg
=
""
# 使用簡單的處理邏輯,有待擴展
if
query_str
=
=
"hello"
:
response_msg
=
"hello to you too"
else
:
response_msg
=
"how are you, unkown"
# 返回消息
# 包括post_msg,和對應的 response_msg
return
pack_text_xml(msg, response_msg);
|
消息迴應,這裏的邏輯很簡單,對於hello,就返回一個hello to you too, 其餘則返回 how are you , unkown。未來用戶若是發來「第一篇」等請求,咱們將返回完整的圖文消息。這部分是有待開發的。
上面的迴應函數用到了打包返回消息成xml這樣的功能,pack_text_xml函數以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 打包消息xml,做爲返回
def
pack_text_xml(post_msg,response_msg):
# f = post_msg['FromUserName']
# t = post_msg['FromUserName']
text_tpl
=
'''<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
<FuncFlag>0</FuncFlag>
</xml>'''
text_tpl
=
text_tpl
%
(post_msg[
'FromUserName'
],post_msg[
'ToUserName'
],
str
(
int
(time.time())),
'text'
,response_msg)
# 調換髮送者和接收者,而後填入須要返回的信息到xml中
return
text_tpl
|
能夠看到text_tpl其實就是微信API內容的一個——返回text,就是聊天記錄同樣的文本。對於其它內容,有其餘內容的模板,能夠到微信公衆賬號API察看。這裏須要說明的是xml結構裏面要填參數的地方,都有一個佔位符號%s,表示的是一個字符串。而後對於模板,填如咱們的參數。如第12行所示。
好了,咱們能夠開始測試了。我google了一下,有人說使用firefox的poster插件,很是好用。因而我就在firefox下裝了一個,來模擬各類各樣不一樣的http請求。首先,要有一個firefox瀏覽器,ubuntu如今是自帶了,無需安裝,若是沒有能夠去官網下載。而後安裝poster,點這裏,找到了poster插件,在firefox安裝就好了。好了,咱們能夠模擬微信服務器發送咱們的消息轉發了,以下:
使用時,填入post請求時發送的xml,還有你的服務器的url,例如個人是: http://scnuwriter.sinaapp.com 而後點post,就能夠看到下面的畫面:
能夠看到咱們的服務器返回了 hello to you too,這是和咱們的程序相符合的。可是可能你獲得的是一個403fobidden。我google了一下,stack overflow裏面說,那多是由於咱們沒有啓用跨站請求,以防止***,具體參考python的csrf文檔。後來我修改了settings.py文件裏面的middleware配置:
1
2
3
4
5
6
7
8
9
10
11
12
|
MIDDLEWARE_CLASSES
=
(
'django.middleware.common.CommonMiddleware'
,
'django.contrib.sessions.middleware.SessionMiddleware'
,
'django.middleware.csrf.CsrfViewMiddleware'
,
'django.contrib.auth.middleware.AuthenticationMiddleware'
,
'django.contrib.messages.middleware.MessageMiddleware'
,
# for post:
#'django.middleware.csrf.CsrfResponseMiddleware',
# Uncomment the next line for simple clickjacking protection:
#'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
|
很奇怪,我去掉第8行CsrfResponseMiddleware以後,服務器就出錯了。故而我註釋了它,而後在views.py的最前面的函數前面加上一個:@csrf_exempt 。結果就好了,獲得上面截圖的內容。要深刻了解這是爲何,還須要瞭解web的更多信息,還有django的官方文檔,bibo也要好好充電了,知其然,不知其因此然,不可謂爲知也。
好吧,用svn上傳你的成果到服務器上吧,用手機來測試。通常用post獲得了正確迴應都不會有錯的。bibo在本身的手機微信上面發送了一個"hello",真的收到了一個"hello to you too"。第一次嘗試微信編程,竟然成功了!yeah。並且還學會了一個工具poster。
by bibodeng 2013-04-19 士別三日,當另眼相看
scnuwriter示例下載連接:http://vdisk.weibo.com/s/y9wJz 主要看views.py