说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!
一丶概论
- 公众号接收与发送消息
验证URL有效性成功后即接入生效,成为开发者。如果公众号类型为服务号(订阅号只能使用普通消息接口),可以在公众平台网站中申请认证,认证成功的服务号将获得众多接口权限,以满足开发者需求。
此后用户每次向公众号发送消息、或者产生自定义菜单点击事件时,开发者填写的服务器配置URL将得到微信服务器推送过来的消息和事件,然后开发者可以依据自身业务逻辑进行响应,例如回复消息等。
用户向公众号发送消息时,公众号方收到的消息发送者是一个OpenID,是使用用户微信号加密后的结果,每个用户对每个公众号有一个唯一的OpenID。
1.接收普通消息
当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。
各消息类型的推送使用XML数据包结构,如:
<xml>
<ToUserName><![CDATA[gh_866835093fea]]></ToUserName>
<FromUserName><![CDATA[ogdotwSc_MmEEsJs9-ABZ1QL_4r4]]></FromUserName>
<CreateTime>1478317060</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
<MsgId>6349323426230210995</MsgId>
</xml>
注意:<![CDATA 与 ]]> 括起来的数据不会被xml解析器解析
2.普通消息类别
- 文本消息
- 图片消息
- 语音消息
- 视频消息
- 小视频消息
- 地理位置消息
- 链接消息
文本消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
3. 回复的消息类型
- 文本消息
- 图片消息
- 语音消息
- 视频消息
- 音乐消息
- 图文消息
回复文本消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
**注:**开发文档可以到 https://mp.weixin.qq.com/wiki/home/index.html 进行阅读查看
二丶代码实现
需求:我们现在来实现一个针对文本消息的收发程序。实现的业务逻辑,关注者发什么内容,我们就传回给什么内容。
说明:微信服务器推送消息还是往/wechat/8007,所以在之前代码上进行修改即可
1.开发步骤
- step1 如何区分微信服务器发过来的是第一次的验证操作还是消息操作
- 验证操作为GET请求,消息操作为POST请求
@app.route("/wechat8007", methods=["GET", "POST"])
- step2 参数变更,echostr参数只是在第一次验证的时候需要,无论是POST请求还是GET请求这三种参数都必要要,因为需要验证是不是微信服务器发送过来的数据
signature = request.args.get("signature")
timestamp = request.args.get("timestamp")
nonce = request.args.get("nonce")
- step3 对微信服务器发送的请求进行验证判断,如果是GET请求,那么代表是第一次的验证操作,那么就需要获取echostr字段的内容,如果内容为空则抛出404,存在则返回echostr
if request.method == "GET":
# 表示是第一次接入微信服务器的验证
echostr = request.args.get("echostr")
if not echostr:
abort(404)
return echostr
- step4 如果为POST请求,那么代表为微信服务器转发消息过来,获取请求中的data xml数据 ,数据为空抛出400
elif request.method == "POST":
# 表示微信服务器转发消息过来
xml_str = request.data
if not xml_str:
abort(400)
- step5 将对获取的数据进行解析,通过xmltodict模块中的parse方法将字符串类型的xml数据,转换成字典类型的xml格式数据,因为xml数据最外层有一个标签,通过get方式获取标签里的内容,再通过get获取内容中的MsgType消息类型字段的值
# 对xml字符串进行解析
xml_dict = xmltodict.parse(xml_str)
xml_dict = xml_dict.get("xml")
# 提取消息类型
msg_type = xml_dict.get("MsgType")
- step6 对消息类型进行判断,如果为text文本消息,则返回文本消息,不是文本消息还是返回文本消息,这里可以拓展为(image,voice,video等等可以查看开发文档),这里为了演示,就简单写写
if msg_type == "text":
# 表示发送的是文本消息
# 构造返回值,经由微信服务器回复给用户的消息内容
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType": "text",
"Content": "taogang say:" + xml_dict.get("Content")
}
}
else:
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType": "text",
"Content": "Dear I Love you so much"
}
}
- step7 最后将我们构造的响应返回值通过unparse方法转换成xml格式的字符串,返回给微信服务器
# 将字典转换为xml字符串
resp_xml_str = xmltodict.unparse(resp_dict)
# 返回消息数据给微信服务器
return resp_xml_str
2.部署测试
- step1 将代码推送到服务器上
- step2 在服务器上进入虚拟环境,运行此程序
- step3 进入微信公众平台,用手机扫描测试号二维码,进行关注测试
扫码后进行关注
关注后进入此公众号,公众号则发送我们在开发步骤step 6,Dear I Love you so much 消息内容
回到服务器程序运行日志上,显示为POST请求,说明程序逻辑完全没问题
公众号测试平台用户列表将我的微信添加上去了
- step4 测试,在关注的此公众中,进行消息(文本,表情,语言,图片,视频)发送,当消息类型为文本时,即返回此消息内容,如果不是都是返回文本类型,内容为Dear I Love you so much
此时的服务器代码运行日志
3.完整代码
# coding:utf-8
from flask import Flask, request, abort, render_template
import hashlib
import xmltodict, time
# 常量
# 微信的token令牌
WECHAT_TOKEN = "cdtaogang"
app = Flask(__name__)
@app.route("/wechat8007", methods=["GET", "POST"])
def wechat():
"""对接微信公众号服务器"""
# 接收微信服务器发送的参数
signature = request.args.get("signature")
timestamp = request.args.get("timestamp")
nonce = request.args.get("nonce")
if not all([signature, timestamp, nonce]):
abort(400)
# 按照微信的流程进行计算签名
li = [WECHAT_TOKEN, timestamp, nonce]
# 排序
li.sort()
# 拼接字符串
tmp_str = ''.join(li)
# 进行sha1加密, 得到正确的签名值
sign = hashlib.sha1(tmp_str).hexdigest()
# 将自己计算的签名值与请求的签名参数进行对比,如果相同,则证明请求来自微信服务器
if sign != signature:
# 表示请求不是微信发的
abort(403)
else:
# 表示是微信发送的请求
if request.method == "GET":
# 表示是第一次接入微信服务器的验证
echostr = request.args.get("echostr")
if not echostr:
abort(404)
return echostr
elif request.method == "POST":
# 表示微信服务器转发消息过来
xml_str = request.data
if not xml_str:
abort(400)
# 对xml字符串进行解析
xml_dict = xmltodict.parse(xml_str)
xml_dict = xml_dict.get("xml")
# 提取消息类型
msg_type = xml_dict.get("MsgType")
if msg_type == "text":
# 表示发送的是文本消息
# 构造返回值,经由微信服务器回复给用户的消息内容
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType": "text",
"Content": "taogang say:" + xml_dict.get("Content")
}
}
else:
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType": "text",
"Content": "Dear I Love you so much"
}
}
# 将字典转换为xml字符串
resp_xml_str = xmltodict.unparse(resp_dict)
# 返回消息数据给微信服务器
return resp_xml_str
if __name__ == '__main__':
app.run(port=8007, debug=True)
评论前必须登录!
注册