关于公众号接收以及回复用户消息的方式有两种, 一种是直接在微信公众号平台进行配置, 请看微信公众平台帮助文档, 另外一种是通过微信服务器转发到后台程序, 再通过微信服务器转发回复的形式, 如下.
首先,微信用户发送消息给公众号, 消息是先是发送到微信服务器, 后面再由微信服务器转发到公众号, 同理, 当公众号收到微信用户消息进行回复时, 也是先发送到微信服务器, 再由微信服务器转发给指定微信用户.
微信公众号可以接收的用户消息类型有这几种:
文本消息(最常见) 图片消息 语音消息 视频消息 小视频消息 地理位置消息 链接消息
以下我们以文本消息为例, 实现微信公众号接收并且回复微信用户消息功能, 关于其他类型消息的接收回复, 可以参考微信公众平台帮助文档.
根据上面图片, 当微信用户发送消息到公众号时, 会先发送到微信服务器, 后续由微信服务器转发到公众号, 由于我们是通过开发者模式进行实现, 所以此处我们需要配置一个服务器地址, 让微信服务器将消息转发到我们设置的服务器地址, 进而提取消息, 进行回复.
首先, 我们需要在微信公众平台进行配置服务器URL以及Token等信息, 在点击 "提交" 时, 微信服务器会发送一个 GET 请求到该服务器URL, 校验该服务器URL能不能正常响应, 如果可以正常响应, 则配置成功, 否则失败!
后续配置成功时, 微信用户发送消息到公众号时, 微信服务器就会以 POST 请求的方式将消息转发到此服务器URL, 届时我们便可以对消息进行提取以及回复.
根据微信公众平台帮助文档提示
参数 | 描述 |
---|---|
signature | 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 |
timestamp | 时间戳 |
nonce | 随机数 |
echostr | 随机字符串 |
后台代码(tornado框架)
配置注意:
URL: 此前听过有网友说过, 服务器URL必须是要域名格式, 不可拼接ip地址, 我也没测试过, 如果有提示错误的可以排除看看
Token: 随便填写, 满足提示要求即可
WECHAT_TOKEN = 上面填写的token
# 假设本接口地址为 http://wodeyuming.com/wechat/control/
class WechatControlHandler(WebBaseHandler):
def get(self):
nonce = self.get_argument("nonce")
echostr = self.get_argument("echostr")
signature = self.get_argument("signature")
timestamp = self.get_argument("timestamp")
# 校验参数是否有空值
if not all([signature, timestamp, nonce, echostr]):
return self.render("error.html", error_msg="参数校验失败!")
# 按照文档提供的方式进行计算签名值,进行比对
sign_list = [WECHAT_TOKEN, timestamp, nonce]
sign_list.sort()
# 拼接字符串
sign_temp_str = "".join(sign_list)
# sha1加密,获取签名值
sign_str = hashlib.sha1(sign_temp_str).hexdigest()
# 验证请求是否来自微信服务器,比较自己生成的签名与微信签名,若相同则表示请求来自微信服务器
if signature == sign_str:
# 微信要求校验成功返回之前发送过来的echostr,原样返回即可, 接入成功
return self.write(echostr)
else:
# 校验失败
return self.render("error.html", error_msg="错误请求!")
到这里, 将URL地址跟WECHAT_TOKEN值填入上述服务器配置, 点击提交, 若提示成功, 则接入成功
注意, 接入成功后上面你配置的Token可能会发生改变, 记得将WECHAT_TOKEN值更换为新生成的Token
再接入成功之后, 微信用户发送给公众号的消息便会被微信服务器通过POST请求方式转发到此服务器地址中, 以文本信息为例, 微信服务器传递过来的参数
1348831860
1234567890123456
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,文本为text |
Content | 文本消息内容 |
MsgId | 消息id,64位整型 |
后台代码(tornado框架)
WECHAT_TOKEN = 上面填写的token
class WechatControlHandler(WebBaseHandler):
def get(self):
......
def post(self):
# 验证请求是否来自微信服务器
nonce = self.get_argument("nonce")
signature = self.get_argument("signature")
timestamp = self.get_argument("timestamp")
sign_list = [WECHAT_TOKEN, timestamp, nonce]
sign_list.sort()
sign_temp_str = "".join(sign_list)
sign_str = hashlib.sha1(sign_temp_str).hexdigest()
if signature == sign_str:
# 请求来自微信服务器, 获取消息, 根据微信公众平台提示, 微信用户发送消息到公众号之后, 不管该消息
# 是否是我们需要处理的, 都要在5秒内进行处理并回复, 否则微信将会给用户发送错误提示, 并且重新进行
# 校验上述服务器URL是否可用, 所以对于我们不需要处理的消息, 可以直接回复 "success"(微信推荐) 或者 "",
# 这样微信服务器将不会发送错误提示到微信, 也不会去重新校验服务器URL
msg_xml_str = self.request.body
if not msg_xml_str:
return self.write("success")
# 解析消息
msg_xml_dict_all = xmltodict.parse(msg_xml_str)
msg_xml_dict = msg_xml_dict_all["xml"]
# 获取消息类型, 消息内容等信息
msg_type = msg_xml_dict["MsgType"]
user_open_id = msg_xml_dict["FromUserName"]
# 需要回复的信息
response_dict = {
"xml": {
"ToUserName": msg_xml_dict["FromUserName"],
"FromUserName": msg_xml_dict["ToUserName"],
"CreateTime": int(time.time()),
"MsgType": "text",
}
}
# 当msg_type消息类型的值为event时, 表示该消息类型为推送消息, 例如微信用户 关注公众号(subscribe),取消关注(unsubscribe)
if msg_type == "event":
# 事件推送消息
msg_event = msg_xml_dict["Event"]
if msg_event == "subscribe":
# 用户关注公众号, 回复感谢信息
response_dict["xml"]["Content"] = "感谢您的关注!"
response_xml_str = xmltodict.unparse(response_dict)
return self.write(response_xml_str)
elif msg_type == "text":
# 文本消息, 获取消息内容, 用户发送 哈哈, 回复 呵呵
msg_body = msg_xml_dict["Content"]
if msg_body == "哈哈":
response_dict["xml"]["Content"] = "呵呵"
response_xml_str = xmltodict.unparse(response_dict)
return self.write(response_xml_str)
# 其他一律回复 success
return self.write("success")
完毕