有了测试账号之后,我们该做什么呢?当然是接收来自于微信的消息和事件。
公众号接收用户消息分为七类,包括文本消息、图片消息、语音消息、视频消息、小视频消息、地理位置消息、链接消息。
公众号接收用户事件分为五类,包括关注事件、取消事件、扫描带参数二维码事件、上报地理位置事件、自定义菜单事件。
当公众号接收到来自微信的消息时,需要做以下几步处理:
1 验证消息是否来源于微信
这个方法在上一节已学习过。
2 解析来自于微信的消息和事件
微信的消息正文是一个XML,我们收到这个XML需要进行解析,wechatpy提供了一个解析XML的方法parse_message,传入XML,返回解析后的对象。我们可以从解析后的对象中获取消息的内容。
from wechatpy import parse_message msg = parse_message(xml)
3 视图编写
视图中的GET方法,上一节已经写好了,这一节主要是POST方法。POST方法主要是判断用户的消息和事件,至于收到这些消息,如何回复,在后面的章节中会讲到。
这里需要注意的一点是用户关注公众号的时候,可以获取到用户的openid。在登录流程中会用到,比如:用户关注公众号的时候,获取用户的openid,保存在数据库中,而用户在登录注册输入手机的页面,再获取一次openid,并在数据库中找到这个Openid,并更新用户的手机号。同时,数据库中保存了Openid,可以随时根据Openid获取用户的其它资料。
from django.views.generic import View from rest_framework import status import logging from django.http import HttpResponse from wechatpy.utils import check_signature from wechatpy.exceptions import InvalidSignatureException from GeneralTools import Constants from wechatpy import parse_message # 获取在配置文件中定义的logger,用来记录日志 logger = logging.getLogger('wec_info') # from . import Replies class WechatInterface(View): """ 微信公众号开发服务器配置 """ @classmethod def get(cls, request): """ 微信服务器验证消息 :param request:GET请求携带参数(signature、timestamp、nonce、echostr) :return:原样返回echostr参数 """ # logger.info("body:%s" % request.body) # logger.info("GET:%s" % request.GET) # logger.info("POST:%s" % request.POST) # 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 signature = request.GET.get('signature') # 时间戳 timestamp = request.GET.get('timestamp') # 随机数 nonce = request.GET.get('nonce') # 随机字符串 echostr = request.GET.get('echostr') logger.info("signature:%s" % signature) logger.info("timestamp:%s" % timestamp) logger.info("nonce:%s" % nonce) logger.info("echostr:%s" % echostr) # 校验参数 if not all([signature, timestamp, nonce, echostr]): # 请求参数错误 return HttpResponse(status.HTTP_400_BAD_REQUEST) try: check_signature( token=Constants.WECHAT_TOKEN, # 开发者在公众号配置中配置的令牌 signature=signature, # 微信加密签名 timestamp=timestamp, # 时间戳 nonce=nonce # 随机数 ) except InvalidSignatureException as e: # 处理异常情况或忽略 logger.error(e) # 微信签名错误 return HttpResponse(status.HTTP_403_FORBIDDEN) # 验证成功时,应原样返回 echostr 参数值 return HttpResponse(echostr) @classmethod def post(cls, request): print('===============>访问到了,') """ 当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。 """ # logger.info("body:%s" % request.body) # logger.info("GET:%s" % request.GET) # logger.info("POST:%s" % request.POST) # 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 signature = request.GET.get('signature') # 时间戳 timestamp = request.GET.get('timestamp') # 随机数 nonce = request.GET.get('nonce') # openid openid = request.GET.get('openid') # logger.info("signature:%s" % signature) # logger.info("timestamp:%s" % timestamp) # logger.info("nonce:%s" % nonce) # openid:o-dlPwDD8x_tKgeinvgj9CY9sfSI # logger.info("openid:%s" % openid) # 校验参数 if not all([signature, timestamp, nonce, openid]): # 请求参数错误 return HttpResponse(status.HTTP_400_BAD_REQUEST) try: check_signature(Constants.WECHAT_TOKEN, signature, timestamp, nonce) except InvalidSignatureException as e: # 处理异常情况或忽略 logger.error(e) # 微信签名错误 return HttpResponse(status.HTTP_403_FORBIDDEN) xml = request.body logger.info("xml:%s" % xml) if not xml: # 请求参数错误 return HttpResponse(status.HTTP_400_BAD_REQUEST) msg = parse_message(xml) # logger.info("msg type:%s" % type(msg)) # logger.info("msg_type:%s" % msg_type) msg_content = None if msg.type == "text": print('====================>') msg_content = '收到文本信息' elif msg.type == 'image': msg_content = '收到图片信息' elif msg.type == 'voice': msg_content = '收到语音信息' elif msg.type == 'video': msg_content = '收到视频消息' print(msg.media_id) elif msg.type == 'shortvideo': msg_content = '收到小视频消息' print(msg.media_id) elif msg.type == 'location': msg_content = '收到地理位置消息' print(msg.label) elif msg.type == 'link': msg_content = '收到链接消息' print(msg.link) elif msg.type == 'event': if msg.event == 'subscribe': # 在用户关注公众号的时候,获取用户的openid,保存在数据库中,而当用户在注册的时候, # 从注册页面再获取一次openid,同时把用户的手机号和openid,并根据该openid更新该用户的手机号。 print('============================>获取用户openid') print(msg.source) print('============================>') msg_content = '用户关注' elif msg.event == 'unsubscribe': print('取消关注公众号') # 取消关注公众号后,不能向用户发送消息,但可以对用户资料进行处理。 elif msg.event == 'subscribe_scan': msg_content = '未关注用户扫描带参二维码事件' elif msg.event == 'scan': msg_content = '已关注用户扫描带参二维码事件' elif msg.event == 'location': msg_content = '上报地理信息事件' elif msg.event == 'click': msg_content = '点击自定义菜单' elif msg.event == 'view': msg_content = '点击菜单跳转事件' else: msg_content = '其它消息' print(msg_content) return HttpResponse('')
4 配置Wechat分路由
from django.urls import path from .views.WechartInterface import WeChatInterface urlpatterns = [ path('WechatInterface/', WeChatInterface.as_view()), ]
5 配置工程总路由
把Wechat分路由加到总路由中
from django.contrib import admin from django.urls import path, include, re_path from rest_framework.documentation import include_docs_urls from TongHeng2 import settings from rest_framework_jwt.views import obtain_jwt_token DESCRIPTION = """ 包括仝恒绩效云所有接口文档。包括以下应用: 1 GeneralTools:常用工具APP 2 Organizations: 组织机构(包含组织下的人员)APP 3 Examples:示例APP,用于新知识的测试案例 """ urlpatterns = [ path('admin/', admin.site.urls), path('Examples/', include('Applications.Examples.urls')), path('Organizations/', include('Applications.Organizations.urls')), path('Wechat/', include('Applications.WeChat.urls')), path('docs/', include_docs_urls(title='API接口文档', description=DESCRIPTION)), path('authorizations/', obtain_jwt_token) ] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
6 把工程发布到指定IP的服务器上
7 测试公众号消息和事件