说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!
在上一篇博客中已经验证了服务器有效性:/qq_41782425/article/details/85321424
一丶概论
公众号接收与发送消息
验证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./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.dataif not xml_str:abort(400)
step5 将对获取的数据进行解析,通过xmltodict模块中的parse方法将字符串类型的xml数据,转换成字典类型的xml格式数据,因为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-8from flask import Flask, request, abort, render_templateimport hashlibimport 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 echostrelif request.method == "POST":# 表示微信服务器转发消息过来xml_str = request.dataif 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_strif __name__ == '__main__':app.run(port=8007, debug=True)