微信公衆號開發之如何實現消息交互

微信開發交流羣:148540125html

系列文章參考地址 極速開發微信公衆號java

歡迎留言、轉發、打賞
項目源碼參考地址 點我點我--歡迎Startgit

前幾篇文章已講完如何導入項目,如何啓動配置項目,如何成爲開發者(若是前三項不會的看這裏 極速開發微信公衆號。這篇文章就來說講若是實現消息交互web

總所周知Jfinal 開發中配置很是簡單隻要在web.xml中添加以下代碼就能夠將全部的請求交由Jfianl處理瀏覽器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <filter>
    <filter-name>jfinal</filter-name>
    <filter-class>com.jfinal.core.JFinalFilter</filter-class>
    <async-supported>true</async-supported>
    <init-param>
      <param-name>configClass</param-name>
      <param-value>com.javen.common.APPConfig</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>jfinal</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

能夠看到com.javen.common.APPConfig 是項目的核心配置文件,他是繼承自JFinalConfig 實現了以下方法微信

消息交互-配置詳解.png

以上配置詳細介紹參考官方文檔
成爲開發者模式這篇文章中講到過消息交互都是由WeixinMsgController接管的,微信開發

消息究竟是如何交互的在此作詳細的講解

上面有講到消息交互都是由WeixinMsgController接管的,她是繼承自MsgControllerAdapter 又繼承自 MsgController 裏面有個index 方法其中上面的攔截器MsgInterceptor是進行加密驗證的(成爲開發者模式),驗證沒有問題就執行index方法,以下圖app

消息交互-接收消息.png

能夠看出接收消息並返回一個InMsg,以後根據信息類型調用對應的抽象方法交給實現方式實現消息的處理。微信公衆平臺

那麼問題來了:
一、如何接收微信交互的xml
二、如何處理微信的各類消息
三、如何響應微信的各類消息async

接收微信交互的xml

成功開發者(get請求)以後,全部的消息接收處理都交由開發者url處理(post請求)因此就有一下方法獲取xml

@Before({NotAction.class})
    public String getInMsgXml() {
        if(this.inMsgXml == null) {
            this.inMsgXml = HttpKit.readData(this.getRequest());
            if(ApiConfigKit.getApiConfig().isEncryptMessage()) {
                this.inMsgXml = MsgEncryptKit.decrypt(this.inMsgXml, this.getPara("timestamp"), this.getPara("nonce"), this.getPara("msg_signature"));
            }
        }

        if(StrKit.isBlank(this.inMsgXml)) {
            throw new RuntimeException("請不要在瀏覽器中請求該鏈接,調試請查看WIKI:http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin-demo%E5%92%8C%E8%B0%83%E8%AF%95");
        } else {
            return this.inMsgXml;
        }
    }

解析微信的各類消息

@Before({NotAction.class})
    public InMsg getInMsg() {
        if(this.inMsg == null) {
            this.inMsg = InMsgParser.parse(this.getInMsgXml());
        }

        return this.inMsg;
    }

能夠看到this.inMsg 爲null時會解析InMsgParser.parse(this.getInMsgXml());獲取到的xml

public static InMsg parse(String xml) {
        XmlHelper xmlHelper = XmlHelper.of(xml);
        return doParse(xmlHelper);
    }

靜態方法 經過xml 實例化一個XmlHelper(主要提供一些經常使用類型數據的獲取方法) 再交給doParse方法處理 text消息 image消息 voice消息 vide消息 shortvideo消息 location消息 link消息 eveen消息

private static InMsg doParse(XmlHelper xmlHelper) {
        String toUserName = xmlHelper.getString("//ToUserName");
        String fromUserName = xmlHelper.getString("//FromUserName");
        Integer createTime = Integer.valueOf(xmlHelper.getNumber("//CreateTime").intValue());
        String msgType = xmlHelper.getString("//MsgType");
        if("text".equals(msgType)) {
            return parseInTextMsg(xmlHelper, toUserName, fromUserName, createTime, msgType);
        } else if("image".equals(msgType)) {
            return parseInImageMsg(xmlHelper, toUserName, fromUserName, createTime, msgType);
        } else if("voice".equals(msgType)) {
            return parseInVoiceMsgAndInSpeechRecognitionResults(xmlHelper, toUserName, fromUserName, createTime, msgType);
        } else if("video".equals(msgType)) {
            return parseInVideoMsg(xmlHelper, toUserName, fromUserName, createTime, msgType);
        } else if("shortvideo".equals(msgType)) {
            return parseInShortVideoMsg(xmlHelper, toUserName, fromUserName, createTime, msgType);
        } else if("location".equals(msgType)) {
            return parseInLocationMsg(xmlHelper, toUserName, fromUserName, createTime, msgType);
        } else if("link".equals(msgType)) {
            return parseInLinkMsg(xmlHelper, toUserName, fromUserName, createTime, msgType);
        } else if("event".equals(msgType)) {
            return parseInEvent(xmlHelper, toUserName, fromUserName, createTime, msgType);
        } else {
            LogKit.error("沒法識別的消息類型 " + msgType + ",請查閱微信公衆平臺開發文檔");
            return parseInNotDefinedMsg(toUserName, fromUserName, createTime, msgType);
        }
    }

解析出來消息類型以後就調用對應的解析方法並返回InMsg

消息類型不少避免重複造輪子,因此就誕生了消息的封裝這個東西。

查看全部普通消息的xml格式找規律進行封裝 官方文檔 能夠發現都包含有ToUserName FromUserName CreateTime MsgId 不一樣的是 MsgType 以及 各個類型對應的消息內容。

這裏是接收消息以及響應消息的截圖
消息交互-消息封裝.png

以解析 text消息 爲栗子講解

接收到的xml 以下

<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName> 
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a test]]></Content>
 <MsgId>1234567890123456</MsgId>
 </xml>

解析text消息

private static InMsg parseInTextMsg(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) {
        InTextMsg msg = new InTextMsg(toUserName, fromUserName, createTime, msgType);
        msg.setContent(xmlHelper.getString("//Content"));
        msg.setMsgId(xmlHelper.getString("//MsgId"));
        return msg;
    }

封裝text消息

public class InTextMsg extends InMsg {
    private String content;
    private String msgId;

    public InTextMsg(String toUserName, String fromUserName, Integer createTime, String msgType) {
        super(toUserName, fromUserName, createTime, msgType);
    }

    public String getContent() {
        return this.content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getMsgId() {
        return this.msgId;
    }

    public void setMsgId(String msgId) {
        this.msgId = msgId;
    }
}

接收消息的公用部分

public abstract class InMsg {
    protected String toUserName;
    protected String fromUserName;
    protected Integer createTime;
    protected String msgType;

    public InMsg(String toUserName, String fromUserName, Integer createTime, String msgType) {
        this.toUserName = toUserName;
        this.fromUserName = fromUserName;
        this.createTime = createTime;
        this.msgType = msgType;
    }

    public String getToUserName() {
        return this.toUserName;
    }

    public void setToUserName(String toUserName) {
        this.toUserName = toUserName;
    }

    public String getFromUserName() {
        return this.fromUserName;
    }

    public void setFromUserName(String fromUserName) {
        this.fromUserName = fromUserName;
    }

    public Integer getCreateTime() {
        return this.createTime;
    }

    public void setCreateTime(Integer createTime) {
        this.createTime = createTime;
    }

    public String getMsgType() {
        return this.msgType;
    }

    public void setMsgType(String msgType) {
        this.msgType = msgType;
    }
}

響應微信的各類消息

由上分析能夠知道,消息處理完成後都是交由抽象方法的實現方法處理消息。MsgControllerAdapter 主要是適配各類消息的抽象類。

下面 text消息爲例子說明

接收到text消息以後會調用 WeixinMsgController中的protected void processInTextMsg(InTextMsg inTextMsg) 方法,能夠經過InTextMsg對象獲取text消息

protected void processInTextMsg(InTextMsg inTextMsg)
  {
    String msgContent = inTextMsg.getContent().trim();
    // 幫助提示
    if ("help".equalsIgnoreCase(msgContent) || "幫助".equals(msgContent)) {
      OutTextMsg outMsg = new OutTextMsg(inTextMsg);
      outMsg.setContent(helpStr);
      render(outMsg);
    }else {
      renderOutTextMsg("你發的內容爲:"+msgContent);
      //轉發給多客服PC客戶端
//      OutCustomMsg outCustomMsg = new OutCustomMsg(inTextMsg);
//      render(outCustomMsg);
    }
    
}

以上能夠看到響應消息有兩種實現方式

第一種render一個消息對象

OutTextMsg outMsg = new OutTextMsg(inTextMsg); 
outMsg.setContent(helpStr); 
render(outMsg);

第二種直接傳一個String

renderOutTextMsg("你發的內容爲:"+msgContent);

如下是具體的實現:
一、將對象轉化爲xml outMsg.toXml()
二、若是是開發模式輸出調試的xml
三、若是是加密模式,就將消息加密
四、經過Jfinal 的renderText()方法應用xml

public void render(OutMsg outMsg) {
        String outMsgXml = outMsg.toXml();
        if(ApiConfigKit.isDevMode()) {
            System.out.println("發送消息:");
            System.out.println(outMsgXml);
            System.out.println("--------------------------------------------------------------------------------\n");
        }

        if(ApiConfigKit.getApiConfig().isEncryptMessage()) {
            outMsgXml = MsgEncryptKit.encrypt(outMsgXml, this.getPara("timestamp"), this.getPara("nonce"));
        }

        this.renderText(outMsgXml, "text/xml");
    }

renderOutTextMsg(String content)方法就是調用的render(outMsg)方法

public void renderOutTextMsg(String content) {
        OutTextMsg outMsg = new OutTextMsg(this.getInMsg());
        outMsg.setContent(content);
        this.render(outMsg);
    }

歡迎留言、轉發、打賞
項目源碼參考地址 點我點我--歡迎Start

相關文章
相關標籤/搜索