上一篇讲了企业号如何开启回调模式,回调模式开启成功后,企业号后台服务器就可以接收和响应客户端发来的消息。客户端发送的消息微信会推送到回调模式中设置的URL中去,接收和响应消息的格式都为xml形式,消息分为以下几种类型:
1348831860 1234567890123456 1
参数 | 说明 |
---|---|
ToUserName | 企业号CorpID |
FromUserName | 成员UserID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:text |
Content | 文本消息内容 |
MsgId | 消息id,64位整型 |
AgentID | 企业应用的id,整型。可在应用的设置页面查看 |
1348831860 1234567890123456 1
参数 | 说明 |
---|---|
ToUserName | 企业号CorpID |
FromUserName | 成员UserID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:image |
PicUrl | 图片链接 |
MediaId | 图片媒体文件id,可以调用获取媒体文件接口拉取数据 |
MsgId | 消息id,64位整型 |
AgentID | 企业应用的id,整型。可在应用的设置页面查看 |
1357290913 1234567890123456 1
参数 | 说明 |
---|---|
ToUserName | 企业号CorpID |
FromUserName | 成员UserID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:voice |
MediaId | 语音媒体文件id,可以调用获取媒体文件接口拉取数据 |
Format | 语音格式,如amr,speex等 |
MsgId | 消息id,64位整型 |
AgentID | 企业应用的id,整型。可在应用的设置页面查看 |
1357290913 1234567890123456 1
参数 | 说明 |
---|---|
ToUserName | 企业号CorpID |
FromUserName | 成员UserID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:video |
MediaId | 视频媒体文件id,可以调用获取媒体文件接口拉取数据 |
ThumbMediaId | 视频消息缩略图的媒体id,可以调用获取媒体文件接口拉取数据 |
MsgId | 消息id,64位整型 |
AgentID | 企业应用的id,整型。可在应用的设置页面查看 |
1351776360 23.134521 113.358803 20 1234567890123456 1
参数 | 说明 |
---|---|
ToUserName | 企业号CorpID |
FromUserName | 成员UserID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:location |
Location_X | 地理位置纬度 |
Location_Y | 地理位置经度 |
Scale | 地图缩放大小 |
Label | 地理位置信息 |
MsgId | 消息id,64位整型 |
AgentID | 企业应用的id,整型。可在应用的设置页面查看 |
注:以上信息摘自微信官方文档(http://qydev.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF)
响应的消息分为以下几种类型:
1348831860
参数 | 说明 |
---|---|
ToUserName | 成员UserID |
FromUserName | 企业号CorpID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:text |
Content | 文本消息内容 |
1348831860
参数 | 说明 |
---|---|
ToUserName | 成员UserID |
FromUserName | 企业号CorpID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:image |
MediaId | 图片文件id,可以调用上传媒体文件接口获取 |
1357290913
参数 | 说明 |
---|---|
ToUserName | 成员UserID |
FromUserName | 企业号CorpID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:voice |
MediaId | 语音文件id,可以调用上传媒体文件接口获取 |
1357290913
参数 | 说明 |
---|---|
ToUserName | 成员UserID |
FromUserName | 企业号CorpID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:video |
MediaId | 视频文件id,可以调用上传媒体文件接口获取 |
Title | 视频消息的标题 |
Description | 视频消息的描述 |
12345678 2
参数 | 说明 |
---|---|
ToUserName | 成员UserID |
FromUserName | 企业号CorpID |
CreateTime | 消息创建时间(整型) |
MsgType | 消息类型,此时固定为:news |
ArticleCount | 图文条数,默认第一条为大图。图文数不能超过10,否则将会无响应 |
Title | 图文消息标题 |
Description | 图文消息描述 |
PicUrl | 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200 |
Url | 点击图文消息跳转链接 |
注:以上信息摘自微信官方文档
(http://qydev.weixin.qq.com/wiki/index.php?title=%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF)
下面我们以接收并响应文本消息为例,说明如何接收和响应消息。
我们都习惯于面向对象编程,所以在写具体逻辑代码之前我们先写几个实体类。我们看到无论是接收的消息,还是响应的消息都有一些共同的属性,所以我们可以把这部分属性提出来放到一个父类里面,由于接收到的消息可以使用另一种方式解析(后面的代码中会体现出来),所以我们只需要转化响应的消息格式即可,代码如下:
package com.weixin.model;
public class MessageBaseResponse {
// 开发者微信号
private String ToUserName;
// 发送方账号(一个OpenID)
private String FromUserName;
// 消息创建时间 (整型)
private long CreateTime;
// 消息类型(text/image/location/link/voice)
private String MsgType;
public String getFromUserName() {
return FromUserName;
}
public void setFromUserName(String fromUserName) {
FromUserName = fromUserName;
}
public long getCreateTime() {
return CreateTime;
}
public void setCreateTime(long createTime) {
CreateTime = createTime;
}
public String getMsgType() {
return MsgType;
}
public void setMsgType(String msgType) {
MsgType = msgType;
}
public String getToUserName() {
return ToUserName;
}
public void setToUserName(String toUserName) {
ToUserName = toUserName;
}
}
package com.weixin.model;
public class MessageTextResponse extends MessageBaseResponse{
// 消息内容
private String Content;
public String getContent() {
return Content;
}
public void setContent(String content) {
Content = content;
}
}
文本消息继承了MessageBaseResponse父类。上一篇我们提到过在开启回调模式的时候,需要用get方式访问我们填写的URL来效验,那么什么时候用post方式呢,就是微信将客户端发送的消息推送到企业号后台服务器的时候,下面附上上一篇文章中servlet的代码,只不过在其中的post提交中加入了一些代码。
package com.weixin.qiye;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.weixin.utils.CoreService;
import com.weixin.utils.WXBizMsgCrypt;
public class CoreServlet extends HttpServlet{
private static final long serialVersionUID = -1580490945554184292L;
public static final String STOKEN = "leonjo";
public static final String SCORPID = "wxf75e841610aaaaaaa";
public static final String SENCODINGAESKEY = "JsIS65d9UAslnaIY3aMRXVOxD6hWMD2tjyx5zOwfR5Q";
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(STOKEN,SENCODINGAESKEY,SCORPID);
// 解析出url上的参数值如下:
String sVerifyMsgSig = request.getParameter("msg_signature");
String sVerifyTimeStamp = request.getParameter("timestamp");
String sVerifyNonce = request.getParameter("nonce");
String sVerifyEchoStr = request.getParameter("echostr");
String sEchoStr; //需要返回的明文
sEchoStr = wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp,
sVerifyNonce, sVerifyEchoStr);
// 验证URL成功,将sEchoStr返回
PrintWriter out = response.getWriter();
out.print(sEchoStr);
} catch (Exception e) {
PrintWriter out = response.getWriter();
out.print(e);
//验证URL失败,错误原因请查看异常
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
try {
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(STOKEN,SENCODINGAESKEY,SCORPID);
String respXml = CoreService.processQiyeRequest(request,wxcpt);
PrintWriter out = response.getWriter();
out.print(respXml);
} catch (Exception e) {
//验证URL失败,错误原因请查看异常
e.printStackTrace();
}
}
}
package com.weixin.utils;
import java.io.StringReader;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.weixin.model.EventQRCode;
import com.weixin.model.MessageQiyeResponse;
import com.weixin.model.MessageTextRequest;
import com.weixin.model.MessageTextResponse;
public class CoreService {
public static String processQiyeRequest(HttpServletRequest request,WXBizMsgCrypt wxcpt){
// XML格式的消息数据
String respXml = null;
// 默认返回的文本消息内容
try {
// 调用parseXml方法解析请求消息
Map requestMap = MessageUtil.parseXml(request);
// 发送方账号
String encrypt = requestMap.get("Encrypt");
// 开发者微信号
String toUserName = requestMap.get("ToUserName");
// 开发者微信号
String agentID = requestMap.get("AgentID");
// 回复文本消息
MessageQiyeResponse messageQiyeResponse = new MessageQiyeResponse();
messageQiyeResponse.setToUserName(toUserName);
messageQiyeResponse.setEncrypt(encrypt);
messageQiyeResponse.setAgentID(agentID);
// 将文本消息对象转换成XML
respXml = MessageUtil.messageToXml(messageQiyeResponse);
String sVerifyMsgSig = request.getParameter("msg_signature");
String sVerifyTimeStamp = request.getParameter("timestamp");
String sVerifyNonce = request.getParameter("nonce");
String sMsg = wxcpt.DecryptMsg(sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, respXml);
String msgType = getMsgType(sMsg);
// 回复文本消息
MessageTextResponse textMessage = new MessageTextResponse();
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(new Date().getTime());
textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
// 文本消息
if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
MessageTextRequest messageTextRequest = (MessageTextRequest) MessageUtil.xmlToObject(new MessageTextRequest(), sMsg);
textMessage.setToUserName(messageTextRequest.getFromUserName());
textMessage.setContent("您输入的是"+messageTextRequest.getContent());
}
// 图片消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {
}
// 语音消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {
}
// 视频消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VIDEO)) {
}
// 地理位置消息
else if (msgType.equals((MessageUtil.REQ_MESSAGE_TYPE_LOCATION))) {
}
// 链接消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {
}
// 事件推送
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
EventQRCode eventQRCode=(EventQRCode) MessageUtil.xmlToObject(new EventQRCode(), sMsg);
textMessage.setToUserName(eventQRCode.getFromUserName());
// 事件类型
String eventType = requestMap.get("Event");
if (eventType==null||eventType.equals("null")) {
textMessage.setContent("欢迎使用GRP系统,请操作下面的菜单来获得您所需要的数据");
}
// 关注
else if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
}
// 取消关注
else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)){
}
//扫描带参数二维码
else if (eventType.equals(MessageUtil.EVENT_TYPE_SCAN)) {
}
// 上报地理位置
else if (eventType.equals(MessageUtil.EVENT_TYPE_LOCATION)) {
}
else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
}
}
respXml = MessageUtil.messageToXml(textMessage);
respXml=wxcpt.EncryptMsg(respXml, sVerifyTimeStamp, sVerifyNonce);
}catch (Exception e) {
e.printStackTrace();
}
return respXml;
}
public static String getMsgType(String xml) throws Exception{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(xml);
InputSource is = new InputSource(sr);
Document document = db.parse(is);
Element root = document.getDocumentElement();
NodeList nodelist1 = root.getElementsByTagName("MsgType");
String msgType = nodelist1.item(0).getTextContent();
return msgType;
}
}
package com.weixin.utils;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.weixin.model.Article;
import com.weixin.model.MessageImageRespone;
import com.weixin.model.MessageMusicResponse;
import com.weixin.model.MessageNewsResponse;
import com.weixin.model.MessageQiyeResponse;
import com.weixin.model.MessageTextResponse;
import com.weixin.model.MessageVideoResponse;
import com.weixin.model.MessageVoiceResponse;
public class MessageUtil {
// 请求消息类型:文本
public static final String REQ_MESSAGE_TYPE_TEXT = "text";
// 请求消息类型:图片
public static final String REQ_MESSAGE_TYPE_IMAGE = "image";
// 请求消息类型:语音
public static final String REQ_MESSAGE_TYPE_VOICE = "voice";
// 请求消息类型:视频
public static final String REQ_MESSAGE_TYPE_VIDEO = "video";
// 请求消息类型:地理位置
public static final String REQ_MESSAGE_TYPE_LOCATION = "location";
// 请求消息类型:链接
public static final String REQ_MESSAGE_TYPE_LINK = "link";
// 请求消息类型:事件推送
public static final String REQ_MESSAGE_TYPE_EVENT = "event";
// 事件类型:subscribe(订阅)
public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
// 事件类型:unsubscribe(取消订阅)
public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
// 事件类型:scan(关注用户扫描带参数二维码)
public static final String EVENT_TYPE_SCAN = "scan";
// 事件类型:LOCATION(上报地理位置)
public static final String EVENT_TYPE_LOCATION = "LOCATION";
// 事件类型:CLICK(自定义菜单)
public static final String EVENT_TYPE_CLICK = "CLICK";
// 响应消息类型:文本
public static final String RESP_MESSAGE_TYPE_TEXT = "text";
// 响应消息类型:图片
public static final String RESP_MESSAGE_TYPE_IMAGE = "image";
// 响应消息类型:语音
public static final String RESP_MESSAGE_TYPE_VOICE = "voice";
// 响应消息类型:视频
public static final String RESP_MESSAGE_TYPE_VIDEO = "video";
// 响应消息类型:音乐
public static final String RESP_MESSAGE_TYPE_MUSIC = "music";
// 响应消息类型:图文
public static final String RESP_MESSAGE_TYPE_NEWS = "news";
/**
* 解析微信发来的请求(XML)
* @param request
* @return
* @throws Exception
*/
public static Map parseXml(HttpServletRequest request) throws Exception {
// 将解析结果存储在HashMap中
Map map = new HashMap();
// 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到XML根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList){
map.put(e.getName(), e.getText());
}
// 释放资源
inputStream.close();
inputStream = null;
return map;
}
/**
* 扩展xstream使其支持CDATA
*/
private static XStream xstream = new XStream(new XppDriver() {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 对所有XML节点的转换都增加CDATA标记
boolean cdata = true;
@SuppressWarnings("unchecked")
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
}
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("");
} else {
writer.write(text);
}
}
};
}
});
/**
* 企业消息对象转换成XML
* @param textMessage
* @return
*/
public static String messageToXml(MessageQiyeResponse messageQiyeResponse) {
xstream.alias("xml", messageQiyeResponse.getClass());
return xstream.toXML(messageQiyeResponse);
}
/**
* XML转换成消息对象
* @param textMessage
* @return
*/
public static Object xmlToObject(Object object,String xml) {
xstream.alias("xml", object.getClass());
return xstream.fromXML(xml);
}
/**
* 文本消息对象转换成XML
* @param textMessage
* @return
*/
public static String messageToXml(MessageTextResponse textMessage) {
xstream.alias("xml", textMessage.getClass());
return xstream.toXML(textMessage);
}
/**
* 图片消息对象转换成XML
* @param imageMessage
* @return
*/
public static String messageToXml(MessageImageRespone imageMessage) {
xstream.alias("xml", imageMessage.getClass());
return xstream.toXML(imageMessage);
}
/**
* 语音消息对象转换成XML
* @param voiceMessage
* @return
*/
public static String messageToXml(MessageVoiceResponse voiceMessage) {
xstream.alias("xml", voiceMessage.getClass());
return xstream.toXML(voiceMessage);
}
/**
* 视频消息对象转换成XML
* @param videoMessage
* @return
*/
public static String messageToXml(MessageVideoResponse videoMessage) {
xstream.alias("xml", videoMessage.getClass());
return xstream.toXML(videoMessage);
}
/**
* 音乐消息对象转换成XML
* @param musicMessage
* @return
*/
public static String messageToXml(MessageMusicResponse musicMessage) {
xstream.alias("xml", musicMessage.getClass());
return xstream.toXML(musicMessage);
}
/**
* 图文消息对象转换成XML
* @param newsMessage
* @return
*/
public static String messageToXml(MessageNewsResponse newsMessage) {
xstream.alias("xml", newsMessage.getClass());
xstream.alias("item", new Article().getClass());
return xstream.toXML(newsMessage);
}
}
最后把class文件提交到百度BAE中,发布,重启,这时候在客户端输入任何文本内容都会收到服务器的自动回复。