微信公众号开发之接收与发送消息

说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

在上一篇博客中已经验证了服务器有效性:https://blog.csdn.net/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.普通消息类别

  1. 文本消息
  2. 图片消息
  3. 语音消息
  4. 视频消息
  5. 小视频消息
  6. 地理位置消息
  7. 链接消息

文本消息

 <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. 回复的消息类型

  1. 文本消息
  2. 图片消息
  3. 语音消息
  4. 视频消息
  5. 音乐消息
  6. 图文消息

回复文本消息

<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数据最外层有一个<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)

 

©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:上身试试 返回首页