微信系列之公眾號被動消息回復
基於Flask和Jinja2開發
被動消息回復
回復success
問題
查詢官方wiki 開頭強調: 假如服務器無法保證在五秒內處理回復,則必須回復“success”或者“”(空串),否則微信后台會發起三次重試。
解釋一下為何有這么奇怪的規定。發起重試是微信后台為了盡可以保證粉絲發送的內容開發者均可以收到。如果開發者不進行回復,微信后台沒辦法確認開發者已收到消息,只好重試。
微信公眾號開發的消息類型共7類
1.文本
2.圖片
3.語音
4.視頻
5.小視頻
6.地理位置
7.鏈接
不同的消息類型其模板各有不同,但共同的模板都包括
參數 | 描述 |
---|---|
ToUserName |
接收者微信號 |
FromUserName |
發送者微信號,若為普通用戶,則為OpenID |
CreateTime |
消息創建時間,時間戳格式 |
MsgType |
消息類型 |
MegId |
消息id, 64位整形 |
如下所示
{# base.xml #}
<xml>
<ToUserName><![CDATA[{{ ToUserName }}]]></ToUserName>
<FromUserName><![CDATA[{{ FromUserName }}]]></FromUserName>
<CreateTime>{{ CreateTime }}</CreateTime>
<MsgType><![CDATA[{{ MsgType }}]]></MsgType>
{% block Message %}{% endblock %}
</xml>
信息接收過程基本解析
1.后台接收微信post
提交的數據,需要注意的是,微信對url
的規定很嚴格。如果服務器配置的反斜杠/
結尾,則微信配置的url
也要添加反斜杠,否則會出現301
重定向錯誤。
2.進行數據解析,如果沒有適合的數據解析方法,則響應success
或者空字符
3.響應特定的數據類型
文本消息
{# text.xml #}
{% extends "base.xml" %}
{% block Message %}
<Content><![CDATA[{{ Content }}]]></Content>
{% endblock %}
圖片消息
微信公眾號官方文檔說明 MediaId
可用來標記特定臨時圖片,可填寫臨時MediaId
或者永久的MediaId
{# image.xml #}
{% extends "base.xml" %}
{% block Message %}
<Image>
<MediaId><![CDATA[{{ MediaId }}]]></MediaId>
</Image>
</xml>
{% endblock %}
完整代碼
# coding: utf-8
# package_name: wx_mp
# 微信公眾號入口
from flask import Blueprint
from flask import request
from wx_mp import receive
from wx_mp import replay
import hashlib
mp = Blueprint('wx_mp', __name__)
def token_check():
signature = request.args.get("signature", '')
timestamp = request.args.get('timestamp', '')
echostr = request.args.get("echostr", '')
nonce = request.args.get("nonce", '')
token = "******"
check_list = [nonce, timestamp, token]
check_list.sort() # 對列表進行非序
sha1 = hashlib.sha1()
list(map(lambda x: sha1.update(x.encode('utf-8')), check_list))
hashcode = sha1.hexdigest()
if signature == hashcode:
return echostr
return ''
def wx_get():
if len(request.args) == 0:
# 無參數,普通請求,直接返回
return "This wechat view"
return token_check()
def wx_post():
recMsg = receive.parse_xml()
replayMsg = replay.get_replay(recMsg)
return replayMsg
@mp.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return wx_get()
elif request.method == 'POST':
return wx_post()
# coding: utf-8
# filename: receive.py
# 解析微信發來的消息
from flask import request
from lxml import etree
class Msg(object):
def __init__(self, xml_data):
self.ToUserName = xml_data.xpath('//ToUserName/text()')[0]
self.FromUserName = xml_data.xpath('//FromUserName/text()')[0]
self.MsgType = xml_data.xpath('//MsgType/text()')[0]
self.MsgId = xml_data.xpath('//MsgId/text()')[0]
self.CreateTime = xml_data.xpath('//CreateTime/text()')[0]
class TextMsg(Msg):
def __init__(self, xml_data):
super().__init__(xml_data)
self.Content = xml_data.xpath('//Content/text()')[0]
class ImageMsg(Msg):
def __init__(self, xml_data):
super().__init__(xml_data)
self.PicUrl = xml_data.xpath('//PicUrl/text()')[0]
self.MediaId = xml_data.xpath('//MediaId/text()')[0]
def parse_xml():
xml_data = etree.XML(request.data)
msg_type = xml_data.xpath('//MsgType/text()')[0]
if msg_type == 'text':
recMsg = TextMsg(xml_data)
elif msg_type == 'image':
recMsg = ImageMsg(xml_data)
return recMsg
# coding: utf-8
# filename: replay.py
# 發送消息給用戶
from wx_mp import receive
from time import time
from flask import render_template
class Msg(object):
def __init__(self, recMsg):
self.ToUserName = recMsg.FromUserName
self.FromUserName = recMsg.ToUserName
self.CreateTime = int(time())
def send(self):
return 'success'
class TextMsg(Msg):
def __init__(self, recMsg):
super().__init__(recMsg)
self.MsgType = 'text'
self.Content = '測試文本'
def send(self):
return render_template('text.xml', **self.__dict__)
class ImageMsg(Msg):
def __init__(self, recMsg):
super().__init__(recMsg)
self.MsgType = 'image'
self.MediaId = recMsg.MediaId
def send(self):
print(self.__dict__)
return render_template('image.xml', **self.__dict__)
def get_replay(recMsg):
try:
if isinstance(recMsg, receive.Msg):
if recMsg.MsgType == 'text':
replayMsg = TextMsg(recMsg)
elif recMsg.MsgType == 'image':
replayMsg = ImageMsg(recMsg)
return replayMsg.send()
else:
return Msg.send()
except Exception as e:
return e.__repr__()