阿里云部署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