Flask微信公众号开发之模板消息(四)
Flask
Flask的全局对象主要有request、g以及Flask()实例——基本上命名为app。request即请求报文相关数据,g可以理解为主要给开发者使用的基于一次请求的全局数据,它们在一次请求结束时会被销毁。而app = Flask(),我理解不会销毁,因为不需要,它是基本的Flask服务数据——肯定不会销毁重建,因为它时刻要处理请求。其他的全局对象还有绑定的第三方组件,比如数据库:db = SQLAlchemy(app)。
首先app = Flask()含有应用配置和路由表数据,配置可以随时设置:app.config['FOO'] ='bar',也可以使用文件导入:app.config.from_pyfile('/a/python/file'),配置中可以设置token、密码、ip、文件路径等;路由表则由app.route('/jm-wechat', methods=['POST']) 返回的装饰器确定,具体类似:
'/jm-wechat' | '/jm-wechat' (url_for(wechat)) | wechat函数对象 |
app会按照路由表逐一调用相关函数对象(一个路由可对应多个函数),从而形成对相关请求的处理,这些函数对象被称作“处理函数”,或“视图函数”。
微信消息和事件
我设置.../jm-wechat作为微信专用请求网址。微信请求的消息和事件有很多分类,比如文字、图片、语音消息,关注、点击菜单事件;它们本质上都是消息,但是有区分。那么如何优雅地对待不同的消息类型?
为此,我受Flask的启发,也设计了一个装饰器用于设置路由表,具体是 h = handler.Handler()对象的route()方法,例如h.route('subscribe')会返回一个装饰器,用于将被装饰函数加入到路由表。
以下是views.py的部分代码:
import threading from fuwuhao import app, db, g, request, access, handler, dbddl, template from fuwuhao.exceptions import * h = handler.Handler() @app.before_request def auth(): resp = access.auth() if resp: return resp
@app.route('/jm-wechat', methods=['POST']) def wechat(): if handler.Handler.parse_xml(request.data) == 'text': handler.Handler.parse_text() ############################################################################# #对于已关注(在服务启用前或者宕机期间),但是没有关注数据的情况,补充关注收据 openid = g.request_msg.get('FromUserName') if g.request_type == 'subscribe': dbddl.entity_integrity(openid=openid, unsubscribed=True) else: dbddl.entity_integrity(openid=openid, subscribed=True) ############################################################################# dbddl.log('jm_wechat.log') return h.handle() @app.route('/jm-admin', methods=['POST']) def admin(): handler.Handler.parse_admin(request.data) dbddl.log('jm_admin.log') return h.handle() @app.route('/jm-dms', methods=['POST']) def dms(): handler.Handler.parse_dms(request.data) dbddl.log('jm_dms.log') return h.handle(user_id=g.user_id) @h.route('subscribe') def subscribe(): '''收到用户的关注,回复欢迎关注文本 ''' reply = '' try: openid = g.request_msg.get('FromUserName') dbddl.add_subscribe_user(openid=openid) reply = '欢迎您的光临!' db.session.commit() except SubsError as se: dbddl.log('error', 'SubsError', se) except BaseException as be: dbddl.log('error', '记录关注信息到数据库时出错', be) if reply: return 'success' return handler.textReply(reply).send() else: return 'success'
handler.Handler.parse_xml()是静态方法,我认为可以节省内存开销。实际上它们不放在类中也可以,调用方式为handler.parse_xml()。
在视图函数wechat()或任何其他视图函数中,始终调用h.handle()处理请求,这就是我设计的路由表的作用,该路由表由@h.route(...)设置,保存在h对象中。
handler.Handler.parse_xml()以及parse_text()都是解析用方法,它们负责解析微信发来的请求报文,将解析得到的请求类型(关注、取消关注、认证等)保存在g.request_type中,粉丝id也保存在g中。
接下来h.handle()会调用路由表中g.request_type对应的处理函数,即用@h.route('subscibe')装饰的函数——subscribe()。
使用路由表,可以让视图函数wechat()非常简洁。而具体处理函数subscribe()等非常独立。