微信開發交流群:148540125
系列文章參考地址 極速開發微信公眾號
歡迎留言、轉發、打賞
項目源碼參考地址 點我點我--歡迎Start
前幾篇文章已講完如何導入項目,如何啟動配置項目,如何成為開發者(如果前三項不會的看這里 極速開發微信公眾號。這篇文章就來講講如果實現消息交互
總所周知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 實現了如下方法

以上配置 詳細介紹參考官方文檔
成為開發者模式這篇文章中講到過消息交互都是由WeixinMsgController接管的,
消息到底是如何交互的在此做詳細的講解
上面有講到消息交互都是由WeixinMsgController接管的,她是繼承自MsgControllerAdapter 又繼承自 MsgController 里面有個index 方法其中上面的攔截器MsgInterceptor是進行加密驗證的(成為開發者模式),驗證沒有問題就執行index方法,如下圖

可以看出接收消息並返回一個InMsg,之后根據信息類型調用對應的抽象方法交給實現方式實現消息的處理。
那么問題來了:
1、如何接收微信交互的xml
2、如何處理微信的各種消息
3、如何響應微信的各種消息
接收微信交互的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 以及 各個類型對應的消息內容。
這里是接收消息以及響應消息的截圖

以解析 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);
以下是具體的實現:
1、將對象轉化為xml outMsg.toXml()
2、如果是開發模式輸出調試的xml
3、如果是加密模式,就將消息加密
4、通過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
