微信tiny框架介绍

源码:https://git.oschina.net/wr_qx/tiny

目录[-]

  • 第一层次:通信处理
  • 第二层次:报文解析
  • 第三层次:报文模拟发送
  • 第四层次:报文模拟接收
  • 第五层次:报文处理框架
  • 第六层次:上下文保持
  • 第七层次:处理的水平扩展能力
  • 第八层次:命令处理框架
  • 第九层次:模板语言的引入
  • 第十层次:模块化
  •      微信对国人而言,想必大名鼎鼎,活跃用户数已经突破6.5亿,足以说明这款应用的生命力。但是使用人数众多,不代表微信的API设计优异,有过微信公众号开发经验的人,想必复杂的报文,众多的服务API以及各种公众号资源与权限设置搞得头痛。其实Tiny框架设计理念之一就是简化开发人员的工作,设计Tiny微信框架可以一定程度上减少一般开发人员的难度。

         前段时间本人写过一篇博文《微信框架的几个层次》,提到了十个层级,介绍之前先说一下微信的消息通讯机制,主要分为被动推送和主动请求两种模式

          一、被动推送模式。此时微信服务器是通讯发起方,用户服务器是通讯接收方。

         微信tiny框架介绍_第1张图片

         这种模式下推送报文分两类:消息和事件。如用户在微信客户端发送的文本消息、图片消息在通讯层面上就是消息报文;而事件报文一般用于处理异步响应,比如用户点击微信菜单触发菜单事件等。

         二、主动请求模式。此时用户服务器是通讯发起方,而微信服务器则是通讯接收方。

        微信tiny框架介绍_第2张图片

          主动请求场景很多,微信开发平台提供的大部分API都是这种模式,如自定义菜单、素材管理、支付等。而微信服务器与微信客户端之间的数据更新有以下两种方式:


    1. 服务器主动推送信息。如微信的群发消息接口,在用户服务器触发群发消息后,由微信服务器往目标客户端主动推送消息。
    2. 客户端拉取信息。如自定义菜单的管理接口,在用户服务器修改了自定义菜单的内容后,微信服务器并不会主动推送内容,而是由微信客户端发现缓存过期后,自行向微信服务器请求更新。

         Tiny微信框架的核心接口如图所示:

    微信tiny框架介绍_第3张图片

     以上接口涵盖了微信通讯、报文转换、消息接收和发送、上下文会话、业务处理等诸多方面,接口说明如下:


    接口
    接口说明
    WeiXinConnector 微信连接管理,管理接收消息和请求消息,同时保持微信的通讯信息(验证令牌和JS访问票据等)
    WeiXinContext 微信上下文环境,支持保存微信的用户会话,也可以记录各个业务处理器的操作结果。
    WeiXinConvert 微信消息/结果转换统一接口,支持优先级排序
    WeiXinHandler 微信业务处理器,支持按优先级排序。按类型可以分为发送和接收处理器。开发人员需要扩展该接口实现业务逻辑。
    WeiXinManager 微信配置管理器,负责加载微信API接口相关参数,和渲染微信URL。
    WeiXinReceiver 微信接收消息器,负责接收微信服务器推送过来的消息和事件,WeiXinConnector委托其接收消息。
    WeiXinSender 微信发送消息器,负责发送消息和上传文件到微信服务器,并处理响应,WeiXinConnector委托其发送消息。
    WeiXinSession 微信用户会话,目前以微信的openId做主键。
    WeiXinSessionManager 微信会话管理器,负责新增、修改和清理微信用户会话。


    第一层次:通信处理


    微信的服务主要是基于HTTP协议,安全通过访问令牌(access_token)保证;少数业务场景使用HTTPS加密协议,甚至涉及安全证书,例如微信商户的支付接口。

    Tiny微信框架的通讯处理由WeiXinConnector总调度,接口定义如下:


    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    public interface WeiXinConnector {
     
         /**
          * 默认的bean配置名称
          */
         public static final String DEFAULT_BEAN_NAME= "weiXinConnector" ;
         
         public static final String ACCESS_TOKEN= "ACCESS_TOKEN" ;
     
         /**
          * 获取当前的管理号客户端信息
          * @return
          */
         Client  getClient();
         
         /**
          * 获得微信消息发送者,负责往微信服务器发送消息
          * @return
          */
         WeiXinSender getWeiXinSender();
         
         /**
          * 获得微信消息接收者,负责解析微信服务器推送过来的消息
          * @return
          */
         WeiXinReceiver getWeiXinReceiver();
         
         /**
          * 获取微信的会话管理者
          * @return
          */
         WeiXinSessionManager  getWeiXinSessionManager();
         
         /**
          * 获取微信验证令牌
          * @return
          */
         AccessToken getAccessToken();
         
         /**
          * 获得微信的JS访问票据
          * @return
          */
         JsApiTicket getJsApiTicket();
         
         /**
          * 发送微信消息
          * @param message
          */
         void send(ToServerMessage message);
         
         /**
          * 上传微信文件
          * @param upload
          */
         void upload(WeiXinHttpUpload upload);
         
         /**
          * 接收微信消息
          * @param request
          * @param response
          */
         void receive(HttpServletRequest request,HttpServletResponse response);
         
    }


    具体到底层的HTTP和HTTPS协议通讯,org.tinygroup.weixinhttp工程提供了业务接口WeiXinHttpConnector实现具体的协议通讯。


    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public interface WeiXinHttpConnector {
     
         /**
          * 默认的bean配置名称
          */
         public static final String DEFAULT_BEAN_NAME= "weiXinHttpConnector" ;
         
          /**
          * 用get方式访问微信URL
          *
          * @param url       要访问的微信URL
          * @return 请求结果
          */
         String getUrl(String url);
         
         /**
          * 用post方式访问微信URL
          *
          * @param url       要访问的微信URL
          * @param content
          * @param cert
          * @return 请求结果
          */
         String postUrl(String url, String content,WeiXinCert cert);
         
         /**
          * 上传文件
          * @param url
          * @param upload
          * @return
          */
         String upload(String url,WeiXinHttpUpload upload);
    }
    目前微信框架实现了HttpClient3.1和HttpClient4.5.1两个版本的底层通讯方案,对开发人员而言只需要配置不同的依赖,而无需关心具体通讯代码。



    第二层次:报文解析


    本人一直对微信的报文设计颇有微词,从整体上看微信报文缺乏统一规范,XML、JSON格式混用,字段命名也不规范。Tiny微信提供WeiXinConvert接口负责报文与对象之间的转换,目前XML报文通过Xsteam转换,JSON报文通过fastjson转换。接口定义如下:


    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    public interface WeiXinConvert extends Comparable<WeiXinConvert> {
         
         /**
          * 获得优先级
          * @return
          */
         int getPriority();
     
         /**
          * 设置优先级
          * @param priority
          */
         void setPriority( int priority);
         
         /**
          * 获得报文的状态
          * @return
          */
         WeiXinConvertMode getWeiXinConvertMode();
         
         /**
          * 获得结果类型
          * @return
          */
         Class<?> getCalssType();
         
         /**
          * 判断转换接口能否处理输入信息(微信报文会出现不同类型报文字段一致的情况,需要根据上下文判断)
          * @param <INPUT>
          * @param input
          * @param context
          * @return
          */
         <INPUT> boolean isMatch(INPUT input,WeiXinContext context);
         
         /**
          * 转换消息(微信报文会出现不同类型报文字段一致的情况,需要根据上下文判断)
          * @param input
          * @return
          */
         <OUTPUT,INPUT> OUTPUT convert(INPUT input,WeiXinContext context);
         
    }


    对开发人员而言,无需思考如何构建复杂的报文,只需依赖对应微信业务模块的子工程,构建不同的消息对象,而底层的报文解析与转换甚至通讯都由tiny框架处理。


     

    第三层次:报文模拟发送


    微信发送报文调试最麻烦的地方就是访问令牌(access_token),这个是根据用户应用动态生成的,而且只保持两个小时有效。Tiny微信框架提供了模拟测试页面,只需要bean配置页面设置相关appId和APP秘钥等参数,开发人员在页面就无需手动输入访问令牌。测试页面如下:

    微信tiny框架介绍_第4张图片


    第四层次:报文模拟接收


    接收报文通常是用来模拟手机端的发送消息,特别是一些复杂交互场景:如命令行菜单,如果每次都通过手机端调试。效率非常低。而通过本测试页面,直接输入模拟的手机报文直接就可以得到报文结果,准确并且快速。模拟页面如图:

    微信tiny框架介绍_第5张图片

    第五层次:报文处理框架


    前面在介绍微信核心接口时提到过WeiXinReceiver和WeiXinSender,分别处理微信推送消息与主动发送消息。但是用户的业务是复杂多变的,Tiny是如何保证微信框架的可扩展性呢?其实WeiXinReceiver和WeiXinSender是由一组有序WeiXinHandler组成,而每一个WeiXinHandler都可以处理一类消息,接口定义如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public interface WeiXinHandler extends Comparable<WeiXinHandler> {
         
         int getPriority();
     
         void setPriority( int priority);
         
         WeiXinHandlerMode getWeiXinHandlerMode();
         
         /**
          * 是否匹配对象和上下文
          * @param <T>
          * @param message
          * @return
          */
         <T> boolean isMatch(T message,WeiXinContext context);
     
         
         /**
          * 处理对象
          * @param <T>
          * @param message
          * @param context
          */
         <T> void process(T message,WeiXinContext context);
    }

    简单举个例子,比如开发一个图片消息处理器ImageMessageHandler,用来处理微信客户端的图片类消息,代码如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class ImageMessageHandler extends AbstractWeiXinHandler{
         public WeiXinHandlerMode getWeiXinHandlerMode() {
             return WeiXinHandlerMode.RECEIVE;
         }
         public <T> boolean isMatch(T message, WeiXinContext context) {
             return message instanceof ImageMessage;
         }
         //具体业务处理
         public <T> void process(T message, WeiXinContext context)  {
             ImageMessage mess = (ImageMessage) message;
              
             //逻辑处理
             TextReplyMessage replyMessage= new TextReplyMessage();
             replyMessage.setContent( "回复图片消息[" +mess.getPicUrl()+ "]" );
             replyMessage.setToUserName(mess.getFromUserName());
             replyMessage.setFromUserName(mess.getToUserName());
             replyMessage.setCreateTime(( int )(System.currentTimeMillis()/ 1000 ));
              
             context.setOutput(replyMessage);
         }
    }



    用户主要是编写isMatch和process这两个函数,前者决定这个业务类能处理哪些微信消息和事件,后者是真正的业务处理类。微信消息的包装和转换由微信框架提供,用户应该关心业务处理逻辑,原则上一个Handler只建议处理一类消息。编写完毕后,需要将Handler配置成bean文件,微信框架就能调用了。

    ImageMessageHandler的作用是接收微信客户端发送的图片类消息,并返回图片地址给用户,效果如下:

    微信tiny框架介绍_第6张图片


    第六层次:上下文保持

    微信是有上下文概念的,比如微信应用的小游戏:猜数字。用户输入一个数字,而服务器告诉用户比目标值偏大还是偏小,直到用户猜中为止。游戏很简单,但是这就涉及到上下文会话,Tiny微信框架提供WeiXinSession接口作为上下文统一接口,而WeiXinSessionManager作为上下文的管理接口存在。


    WeiXinSession接口定义如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    public interface WeiXinSession extends Serializable{
     
         /**
          * 会话Id
          * @return
          */
         String getSessionId();
         
         /**
          * 是否包含某元素
          * @param name
          * @return
          */
         boolean contains(String name);
         
         /**
          * 返回指定name的序列化对象
          * @param <T>
          * @param name
          * @return
          */
         <T extends Serializable> T getParameter(String name);
         
         /**
          * 设置序列化的参数对象
          * @param <T>
          * @param name
          * @param value
          */
         <T extends Serializable> void setParameter(String name,T value);
         
          /**
          * 取得session的创建时间。
          *
          * @return 创建时间戮
          */
         long getCreationTime();
     
         /**
          * 取得最近访问时间。
          *
          * @return 最近访问时间戮
          */
         long getLastAccessedTime();
     
         /**
          * 取得session的最大不活动期限,超过此时间,session就会失效。
          *
          * @return 不活动期限的秒数,0表示永不过期
          */
         int getMaxInactiveInterval();
         
         /**
          * 设置session的最大不活动期限,单位秒
          * @param maxInactiveInterval
          */
         void setMaxInactiveInterval( int maxInactiveInterval);
         
         /**
          * 判断session有没有过期。
          *
          * @return 如果过期了,则返回<code>true</code>
          */
         boolean isExpired();
         
         /**
          * 更新session
          */
         void update();
    }



    WeiXinSessionManager管理接口主要提供创建、删除、查询上下文会话的操作API,接口定义如下:


    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    public interface WeiXinSessionManager {
         
         /**
          * 默认的bean配置名称
          */
         public static final String DEFAULT_BEAN_NAME= "weiXinSessionManager" ;
         
         /**
          * 创建会话
          * @param sessionId
          * @return
          */
         WeiXinSession createWeiXinSession(String sessionId);
         /**
          * 查询会话
          * @param sessionId
          * @return
          */
         WeiXinSession getWeiXinSession(String sessionId);
         
         /**
          * 添加会话
          * @param session
          */
         void addWeiXinSession(WeiXinSession session);
         
         /**
          * 手动删除会话
          * @param sessionId
          * @return
          */
         void removeWeiXinSession(String sessionId);
         
         /**
          * 遍历会话
          * @return
          */
         WeiXinSession[] getWeiXinSessions();
         
         /**
          * 清理会话过期的Session
          */
         void expireWeiXinSessions();
         
         /**
          * 清理全部Session
          */
         void clear();
         
         /**
          * Session最大过期时间设置,单位s,默认0
          * @return
          */
         int getMaxInactiveInterval();
         
         /**
          * Session清理线程首次延迟时间,单位s,默认值60
          * @return
          */
         int getExpireTimerDelay();
         
         /**
          * Session清理线程运行周期,单位s,默认值300
          * @return
          */
         int getExpireTimePeriod();
    }



    当然Tiny微信框架提供了上下文相关接口,不代表每一类消息强制进行会话管理,比如简单文本消息,微信客户端的位置消息,完全可以请求-响应这种模式进行处理。


    第七层次:处理的水平扩展能力

    前文讲过Tiny微信框架的具体业务是由WeiXinHandler接口完成的,而WeiXinHandler接口是不依赖WeiXinConnector等委托对象,因此通过扩展WeiXinHandler接口完全可以实现处理能力的水平扩展,比如Tiny框架本身有服务中心,支持分布式服务,那么我们可以在WeiXinHandler接口包装服务中心,从而实现分布式服务。

    第八层次:命令处理框架

    实际上一个微信公众号,许多的时候都是通过使用者用文字(语音识别也归到用文字)的方式与平台进行交互,这个时候,其实就是一个命令行的处理。Tiny微信框架通过org.tinygroup.menucommand实现相关需求,开发人员只需要配置XML就可完成复杂的命令行处理。目前支持两种模式:

    1. 简单模式。用户不需要动态数据,仅需要配置即可。类似电话黄页,支持逐级递归,针对这种模式,Tiny支持配置方式,用户无需编码。
    2. 动态模式。用户除了静态数据,还涉及动态交互,无法通过配置解决。Tiny框架提供了MenuCommandHandler接口,然后在配置中指定具体类或bean,框架就能解决。


    配置文件是以menuconfig.xml为结尾,以演示工程的command.menuconfig.xml为例:


    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    <!-- 菜单命令节点支持多个菜单配置节点和系统命令节点 -->
    < menu-configs >
         <!-- 菜单配置节点可以嵌套,支持定义子菜单和菜单命令节点 -->
         < menu-config id = "m001" name = "menu" title = "功能目录" >
             < regex > <![CDATA[m|menu|菜单]]> </ regex >
             < description > <![CDATA[微信服务列表]]> </ description >
             < menu-config id = "g001" name = "guess" title = "数字竞猜" path = "/game/guessNumber.page" >
                 < regex > <![CDATA[guess|猜数字]]> </ regex >
                 < description > <![CDATA[猜数字小游戏,输入guess或者猜数字]]> </ description >
                 < menu-command name = "new" title = "新建游戏" event-type = "enter"
                     class-name = "org.tinygroup.weixinservice.commandhandler.NewGuessGameHandler" >
                     < regex > <![CDATA[new|新游戏]]> </ regex >
                     < description > <![CDATA[输

    你可能感兴趣的:(微信tiny框架介绍)