微信公众号开发

流程分析

公众号开发,可能在3、4年前就很多人在弄了,可能现在都主要在做小程序开发了。但是,另外一个原因就是,公众号生态现在都比较成熟了,不像刚出来时候的热度。举个例子,就是说很多互联网的产物,都有一个生命周期,从创造、热门、稳定、成熟,这样慢慢淘汰、沉淀下来。好像说跑了,好吧,直入主题。

来说说微信接入流程吧,包括一个消息回复流程和时间监听流程

消息回复流程:

1. 公众号开发者配置,会发送一个Get请求到我的服务里,执行服务验证
2. 微信用户发送消息   --->  微信服务器  --->  我的服务
3. 我的服务回复消息   --->  微信服务   ---> 微信用户

事件监听流程:

1. 微信用户执行操作(关注等)   --->  微信服务器  --->  我的服务
2. 我的服务   --->   主动发送消息或者记录

环境配置

本地开发环境,需要使用内网穿透,不然就需要更新到线上地址。于是,我使用了 ngrok 工具,并且需要使用 Nginx,配置服务,因为微信消息的回调接口只支持80端口

看了几个文章,包括这篇使用Nginx代理Ngrok,后来觉得,官网一个很简洁的方法,测试过,Mac 和 Ubuntu系统,配置起来还是非常方便的,地址在这里

使用Mac时候,因为默认80端口被htppd占用,Nginx使用80端口,需要一个教程,在这里

这里贴上一个接口支持列表

公众平台接口权限列表说明

系统开发

很简单,一个 GET请求和 POST请求,前者用来验证用户系统,后来用来接收用户消息(内容和事件)

from django.http import HttpResponse
from django.conf import settings
from wechatpy.utils import check_signature
from wechatpy import parse_message, create_reply


def index(request):

    # GET方法用户微信公众号绑定验证
    if request.method == 'GET':
        signature = request.GET.get('signature', '')
        timestamp = request.GET.get('timestamp', '')
        nonce = request.GET.get('nonce', '')
        echo_str = request.GET.get('echostr', '')
        try:
            check_signature(settings.TOKEN, signature, timestamp, nonce)
        except InvalidSignatureException:
            echo_str = 'error'
        response = HttpResponse(echo_str, content_type="text/plain")
        return response

    # POST方法用于接受消息和返回消息
    if request.method == 'POST':
        reply = None
        msg = parse_message(request.body)
        print('msg', msg)
        if msg.type == 'text':
            reply = create_reply(handle_text(msg), msg)
        elif msg.type == 'event':
            reply = create_reply(handle_event(msg), msg)
        else:
            pass

        if not reply or not isinstance(reply, BaseReply):
            reply = create_reply('暂不支持您发送的消息类型', msg)

        response = HttpResponse(reply.render(), content_type="application/xml")
        return response

这里 handle_texthandle_event 两个方法,分别处理内容和事件

另外使用 wechatpy 模块,实现一下主动调用的接口,定义一个 WC类,并且一个捕捉错误的装饰器 wc

def wc_error(func):
    # 包装器接受所有参数
    @functools.wraps(func)
    def wraps(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except WeChatClientException as e:
            if settings.DEBUG:
                print(e)
            else:
                args = list(args)
                if args and isinstance(args[0], WC):
                    args.pop(0)
                settings.APP_LOGGER('warning').info(json.dumps({
                    'method': 'wc_error',
                    'args': args,
                    'kwargs': kwargs,
                    'error': str(e)
                }))
        except Exception as e:
            if settings.DEBUG:
                print(e)
            else:
                args = list(args)
                if args and isinstance(args[0], WC):
                    args.pop(0)
                settings.APP_LOGGER('warning').info(json.dumps({
                    'method': 'wc_error',
                    'args': args,
                    'kwargs': kwargs,
                    'error': str(e)
                }))
    return wraps

注意这里是一个实例方法,args第一个参数,很可能是wc objcet,所以需要额外处理

总结

下面是一些注意事项吧,个人总结:

  1. 回复消息:项目部分操作,例如合成图片,需要耗时比较长,所以使用了 Celery 异步任务队列。
  2. 连接微信时候,Wechatpy 使用 Redis 缓存 access token,防止每次启动都获取新的access token,并且自动更新
  3. wechatpy部分文档找不到调用方法,直接看源代码

以前也做过微信开发,但这次理解得更加透彻,对于整个流程,接口调用等。后续写一下,这个项目,关于裂变活动的部分。