在上篇博客阿里雲部署django實現公網訪問已經實現了了django在阿里雲上的部署,接下來記錄django實現微信公眾號簡單回復的開發過程,以方便日后查看
內容概要:
(1)微信公眾號聲請
(2)微信公眾號開發者配置
(3)文本回復實現
(4)圖片回復實現
1. 微信公眾號聲請
微信公眾號的申請就不作介紹了,參考微信公眾平台開發者文檔中的入門指引
2. 微信公眾號開發者配置
開發者配置是微信公眾號開發的第一步,顯得極其重要
公眾平台官網登錄之后,找到“基本配置”菜單欄,如下圖:
重點說明URL(服務器地址的配置),即與微信服務器直接通訊的服務器地址,我這里設置的是http://外網ip/wx/
同時django中的配置如下:(說明:我的django工程為mysite,微信應用為wechat)
(1)mysite目錄下的urls.py配置如下
#from django.contrib import admin #from django.urls import path #from django.conf.urls import include,url from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^blog/', include(('blog.urls',"blog"),namespace="blog")), url(r'^account/', include(('account.urls','account'),namespace='account')), url(r'^wx/', include(('wechat.urls','wechat'),namespace='wechat')), ]
(2)wechat目錄下的urls.py配置如下
from django.conf.urls import url from .views import WeChat urlpatterns = [url(r'^$', WeChat.as_view())]
注:第一次我的URL配置為http://外網ip/wx,但在進行微信回復時提示"You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SL.....",百度后將修改settings:APPEND_SLASH=False也沒有成功,后將配置改為http://外網ip/wx/成功了,若大家遇到同樣的問題,可以多做嘗試,主要原因還是因為表單的提交要將from的action地址改為/結尾
(3)token驗證
token驗證流程如下圖:
代碼實現:
# Create your views here. # -*- coding: utf-8 -*- from django.shortcuts import render from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from django.views.generic.base import View from django.template import loader, Context from xml.etree import ElementTree as ET import time import hashlib from .analysis import Analysis from django.utils.encoding import smart_str class WeChat(View): #這里我當時寫成了防止跨站請求偽造,其實不是這樣的,恰恰相反。因為django默認是開啟了csrf防護中間件的 #所以這里使用@csrf_exempt是單獨為這個函數去掉這個防護功能。 @csrf_exempt def dispatch(self, *args, **kwargs): return super(WeChat, self).dispatch(*args, **kwargs) #微信的介入驗證是GET方法 #微信正常的收發消息是POST方法 @csrf_exempt def get(self, request): print("welcome wx") #下面這四個參數是在接入時,微信的服務器發送過來的參數 signature = request.GET.get('signature', None) #print(signature) timestamp = request.GET.get('timestamp', None) nonce = request.GET.get('nonce', None) echostr = request.GET.get('echostr', None) #這個token是我們自己來定義的,並且這個要填寫在開發文檔中的Token的位置 token = 'fateli' #把token,timestamp, nonce放在一個序列中,並且按字符排序 hashlist = [token, timestamp, nonce] hashlist.sort() #將上面的序列合成一個字符串 hashstr = ''.join([s for s in hashlist]) #通過python標准庫中的sha1加密算法,處理上面的字符串,形成新的字符串。 s1 = hashlib.sha1() s1.update(hashstr.encode("utf8")) hashstr = s1.hexdigest() #print(hashstr) #把我們生成的字符串和微信服務器發送過來的字符串比較, #如果相同,就把服務器發過來的echostr字符串返回去 if hashstr == signature: return HttpResponse(echostr) else: return HttpResponse("field")
配置成功后就可以開始后續的消息回復工作了。若出現為問題,一定要仔細閱讀開發者文檔說明。
3. 文本回復實現
回復的實現主要是要清除協議,其后就很簡單了。
(1)接受文本格式
<xml> <ToUserName><![CDATA[公眾號]]></ToUserName> <FromUserName><![CDATA[粉絲號]]></FromUserName> <CreateTime>1460537339</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[歡迎開啟公眾號開發者模式]]></Content> <MsgId>6272960105994287618</MsgId> </xml>
(2)回復文本格式
<xml>
<ToUserName><![CDATA[粉絲號]]></ToUserName>
<FromUserName><![CDATA[公眾號]]></FromUserName>
<CreateTime>1460541339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[test]]></Content>
</xml>
(3)代碼實現
新建analysis.py
from xml.etree import ElementTree as ET import time class Analysis: def __init__(self, xmlData): print("接收到的數據:" + xmlData) def prase(self, xmlText): xmlData = ET.fromstring(xmlText) msgType = xmlData.find("MsgType").text toUserName = xmlData.find("ToUserName").text fromUserName= xmlData.find("FromUserName").text if msgType == 'text': content = xmlData.find("Content").text TextMsgObj = TextMsg(toUserName, fromUserName, content) return TextMsgObj.structReply() elif msgType == 'image': mediaId = xmlData.find("MediaId").text ImageMsgObj = ImageMsg(toUserName,fromUserName,mediaId) return ImageMsgObj.structReply() class TextMsg: def __init__(self,toUser,fromUser,recvMsg): self._toUser = toUser self._fromUser = fromUser self._recvMsg = recvMsg self._nowTime = int(time.time()) def structReply(self): content = self._recvMsg text = """ <xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[{3}]]></Content> </xml> """.format(self._fromUser, self._toUser,self._nowTime,content) #前面兩個參數的順序需要特別注意 return text
POST代碼如下:
@csrf_exempt def post(self, request): print("POST請求") analysisObj = Analysis(smart_str(request.body)) toWxData = analysisObj.prase(smart_str(request.body)) print(toWxData) return HttpResponse(smart_str(toWxData))
4. 圖片回復實現
實現了文本回復后圖片恢復也就很簡單了,過程一樣,只是協議字段有區別
(1)接受文本格式
<xml> <ToUserName><![CDATA[公眾號]]></ToUserName> <FromUserName><![CDATA[粉絲號]]></FromUserName> <CreateTime>1460536575</CreateTime> <MsgType><![CDATA[image]]></MsgType> <PicUrl><![CDATA[http://mmbiz.qpic.cn/xxxxxx /0]]></PicUrl> <MsgId>6272956824639273066</MsgId> <MediaId><![CDATA[gyci5a-xxxxx-OL]]></MediaId> </xml>
(2)回復文本格式
<xml>
<ToUserName><![CDATA[粉絲號]]></ToUserName>
<FromUserName><![CDATA[公眾號]]></FromUserName>
<CreateTime>1460536576</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[gyci5oxxxxxxv3cOL]]></MediaId>
</Image>
</xml>
注意回復文本格式中只有MediaId,后續博客進行說明
(3)代碼實現
class ImageMsg: def __init__(self,toUser,fromUser,mediaId): self._toUser = toUser self._fromUser = fromUser self._rediaId = mediaId self._nowTime = int(time.time()) self._mediaId = mediaId def structReply(self): text = """ <xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime> <MsgType><![CDATA[image]]></MsgType> <Image> <MediaId><![CDATA[{3}]]></MediaId> </Image> </xml> """.format(self._fromUser, self._toUser,self._nowTime,self._mediaId) #前面兩個參數的順序需要特別注意 return text
在開發過程中遇到問題,可以使用微信公眾平台提供的在線接口調試工具。
原計划是繼續進行菜單項的開發,但由於是個人訂閱號,無法卡通認證,也就無法獲取API開發權限,目前只能到此。