在上篇博客阿里雲部署django實現公網訪問已經實現了了django在阿里雲上的部署,接下來記錄django實現微信公衆號簡單回覆的開發過程,以方便往後查看html
內容概要:python
(1)微信公衆號聲請算法
(2)微信公衆號開發者配置django
(3)文本回復實現服務器
(4)圖片回覆實現微信
微信公衆號的申請就不做介紹了,參考微信公衆平臺開發者文檔中的入門指引微信公衆平臺
開發者配置是微信公衆號開發的第一步,顯得極其重要函數
公衆平臺官網登陸以後,找到「基本配置」菜單欄,以下圖:工具
重點說明URL(服務器地址的配置),即與微信服務器直接通信的服務器地址,我這裏設置的是http://外網ip/wx/post
同時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")
配置成功後就能夠開始後續的消息回覆工做了。若出現爲問題,必定要仔細閱讀開發者文檔說明。
回覆的實現主要是要清除協議,其後就很簡單了。
(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))
實現了文本回復後圖片恢復也就很簡單了,過程同樣,只是協議字段有區別
(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開發權限,目前只能到此。