深入解析微信消息路由器及WxMpMessageHandler实现方法

2024-12-18 配置与搭建 152次阅读

微信公众号的开发中,消息处理至关重要。微信会发送各式各样的消息,公众号需作出相应应对,这其中涉及众多技巧。例如,如何高效应对不同类型的消息,以及如何提升access_token获取的效率等问题。这些都是开发公众号时必须掌握的核心知识。

    @RequestMapping("send")
    public String send(@RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce) {
        // 校验签名
        if (!wxMpService.checkSignature(timestamp, nonce, signature)) {
            log.error("签名校验 ===》 非法请求");
            // 消息签名不正确,说明不是公众平台发过来的消息
            return null;
        }
        log.error("签名校验 ===》 验证成功");
        // 解析消息体,封装为对象
        WxMpXmlMessage xmlMessage = WxMpXmlMessage.fromXml(requestBody);
        // 接收消息内容
        String inContent = xmlMessage.getContent();
        // 响应的消息内容
        String outContent;
        // 根据不同的关键字回复消息
        if (inContent.contains("hello")) {
            outContent = "hello world";
        } else if (inContent.contains("java")) {
            outContent = "hello java";
        } else {
            outContent = "服务繁忙,暂时不能回复";
        }
        // 构造响应消息对象
        WxMpXmlOutTextMessage outTextMessage = WxMpXmlOutMessage.TEXT().content(outContent).fromUser(xmlMessage.getToUser())
                .toUser(xmlMessage.getFromUser()).build();
        // 将响应消息转换为xml格式返回
        return outTextMessage.toXml();
    }

不同消息类型的回复对象

在这里插入图片描述

信息种类繁多,对于各类信息,存在一种特别的回复对象,称作WxMpXmlOutTextMessage。这相当于一座桥梁,将公众号与用户紧密相连。例如,当用户发送文本或其它形式的信息时,开发者可通过构建此对象的不同方式来作出回应。在实际的开发过程中,开发者需依据信息类型来决定如何构建该对象,以向用户提供恰当的反馈。此外,在某些情况下,信息类型难以辨识,这时开发者可能需要投入更多精力来处理这些问题。

信息种类繁多,公众号需作出相应应对。以小商家公众号为例,用户可能咨询产品或反映问题,若处理不当,用户满意度将大打折扣。因此,对各类信息作出精确回应是提高服务质量的关键。

msgType
event
eventKey
content

微信消息路由器的作用

WxMpMessageRouter这个消息路由器真是个好帮手。微信推送的公众号消息种类繁多,若用if/else逐一判断,确实挺麻烦。而这个路由器却能从四个方面对消息进行匹配。例如,某些公众号拥有多种功能模块,每个模块对应不同的消息,这时,消息路由器便能准确地将消息导向相应的处理区域。

比如,我了解一个提供资讯的公众号,它设有多个栏目,比如时事报道、娱乐新闻等。这些栏目对应着不同类型的消息,而通过消息路由器,这些消息能准确分配到相应的栏目进行管理。这样一来,公众号处理信息的速度得到了显著提升,同时,整个系统的结构也更加清晰和科学。

public interface WxMpMessageHandler {
  /**
   *
   * @param wxMessage
   * @param context  上下文,如果同一个路由规则内的handler或interceptor之间有信息要传递,可以用这个
   * @param wxMpService
   * @return xml格式的消息,如果在异步规则里处理的话,可以返回null
   */
  public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
                                  Map context,
                                  WxMpService wxMpService,
                                  WxSessionManager sessionManager);
}

消息处理器的实现

针对不同种类的信息处理,我们需要自行编写符合WxMpMessageHandler接口的消息处理器。以用户询问订单信息为例,我们需通过实现该接口来应对此类信息。这实际上是针对具体业务需求进行消息定制处理的关键环节。由于各个业务场景各不相同,消息处理器必须作出适当的调整。

public interface WxMpMessageInterceptor {
  /**
   * 拦截微信消息
   * @param wxMessage
   * @param context  上下文,如果handler或interceptor之间有信息要传递,可以用这个
   * @param wxMpService
   * @return  true代表OK,false代表不OK
   */
  public boolean intercept(WxMpXmlMessage wxMessage,
                           Map context,
                           WxMpService wxMpService,
                           WxSessionManager sessionManager);
}

一些在线商城的公众号,买家下单或查询物流时,需要不同的消息处理器。若接口实现不当,消息处理将出现问题,可能引发订单错误或物流信息混乱,给业务带来不便。

添加拦截器处理消息

@Component
public class MyTextHandler implements WxMpMessageHandler {
    @Override
    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
        // 接收消息内容
        String inContent = wxMessage.getContent();
        // 响应的消息内容
        String outContent;
        // 根据不同的关键字回复消息
        if (inContent.contains("hello")) {
            outContent = "hello world";
        } else if (inContent.contains("java")) {
            outContent = "hello java";
        } else if (inContent.contains("***")) {
            outContent = "请文明用语";
        } else {
            outContent = "服务繁忙,暂时不能回复";
        }
        // 构造响应消息对象
        return WxMpXmlOutMessage.TEXT().content(outContent).fromUser(wxMessage.getToUser())
                .toUser(wxMessage.getFromUser()).build();
    }
}

偶尔,我们可引入WxMessageInterceptor拦截器。当处理微信公众账号信息时,它能对信息进行预处理和筛选。需自行编写拦截处理器,并实现WxMessageHandler接口。例如,若公众账号接收到恶意或广告信息,拦截器能先行过滤。

/**
 * 对微信公众号消息进行预处理、过滤等操作,根据具体业务需求决定是否允许继续执行后面的路由处理方法
 * 

* 如果要中止消息的继续处理,即表示拦截了这个消息,需要返回 false。否则,在执行完当前拦截器操作后,允许消息的继续处理,返回 true */ @Component public class MyTextInterceptor implements WxMpMessageInterceptor { @Override public boolean intercept(WxMpXmlMessage wxMpXmlMessage, Map map, WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException { String msg = wxMpXmlMessage.getContent(); String msgType = wxMpXmlMessage.getMsgType(); if (msgType.equals("text") && msg.contains("混蛋")) { wxMpXmlMessage.setContent("***"); return true; } return true; } }

某公司的客服公众号常收到无关紧要的广告信息。若安装拦截工具,便能阻止这些信息进入后续的繁琐处理步骤,仅将客户咨询和投诉等有用信息送达指定处理环节,从而既节约了资源,又提高了工作效率。

消息路由的配置与使用

为了处理不同种类的信息,我们依赖消息路由对象WxMpMessageRouter。在Controller中注入此对象,即可将信息精确地导向相应的处理器。以一个提供多业务的服务公众号为例,如外卖点餐、家政等,这种配置能确保各业务相关的信息被准确送达至对应的处理器,从而有序推进各项业务。

@Configuration
public class MessageRouterConfig {
    @Autowired
    private WxMpService wxMpService;
    @Autowired
    private MyTextHandler textHandler;
    @Autowired
    private MyTextInterceptor textInterceptor;
    @Bean
    public WxMpMessageRouter messageRouter() {
        // 创建消息路由
        final WxMpMessageRouter router = new WxMpMessageRouter(wxMpService);
        // 添加一个同步处理文本消息的路由规则 同时添加interceptor、handler
        router.rule().async(false).msgType(WxConsts.XmlMsgType.TEXT).interceptor(textInterceptor).handler(textHandler).end();
        return router;
    }
}

在实际情况中,若这一环节处理不当,信息便会陷入混乱。例如,外卖订单的信息误入家政服务的处理系统,整个业务流程便会陷入混乱。

access_token的持久化

@Slf4j
@RestController
public class TestController {
    @Autowired
    private WxMpService wxMpService;
    @Autowired
    private WxMpMessageRouter wxMpMessageRouter;
    @RequestMapping("send")
    public String configAccess(@RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce) {
        // 校验签名
        if (!wxMpService.checkSignature(timestamp, nonce, signature)) {
            log.error("签名校验 ===》 非法请求");
            // 消息签名不正确,说明不是公众平台发过来的消息
            return null;
        }
        log.error("签名校验 ===》 验证成功");
        // 解析消息体,封装为对象
        WxMpXmlMessage xmlMessage = WxMpXmlMessage.fromXml(requestBody);
        WxMpXmlOutMessage outMessage = null;
        try {
            // 将消息路由给对应的处理器,获取响应
            outMessage = wxMpMessageRouter.route(xmlMessage);
        } catch (Exception e) {
            log.error("消息路由异常", e);
        }
        // 将响应消息转换为xml格式返回
        return outMessage == null ? null : outMessage.toXml();
    }
 }

微信公众号开发中,对access_token的管理颇为关键。WxMpConfigStorage负责存储公众号的相关信息,其中的access_token尤为关键。微信API在获取此token时有限制,若每次交互都重新获取,不仅效率低下,还可能遭遇获取失败的风险。在分布式环境中,这一问题更为突出。

在这里插入图片描述

大型互联网公司通常拥有众多子项目,其中不少是微信公众号。若要分别获取每个服务的资源,既繁琐又浪费。因此,我们可以将access_token长期保存在redis中。这就像将宝藏存放在保险库,需要时直接取用,从而大幅提高整体效率。但若使用redisTemplate的type,则需要额外引入相应的依赖。

在使用微信公众号开发服务的各位朋友中,是否有人遇到过消息处理上的混乱问题,或者是在获取access_token时遇到了麻烦?不妨点个赞,分享这篇文章,并在评论区交流讨论。

声明:演示站所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流。若您的权利被侵害,请联系
微信扫码,联系我们