微信企业号开发之接收响应消息

上一篇讲了企业号如何开启回调模式,回调模式开启成功后,企业号后台服务器就可以接收和响应客户端发来的消息。客户端发送的消息微信会推送到回调模式中设置的URL中去,接收和响应消息的格式都为xml形式,消息分为以下几种类型:

text消息


   
    
   1348831860
   
   
   1234567890123456
   1

参数 说明
ToUserName 企业号CorpID
FromUserName 成员UserID
CreateTime 消息创建时间(整型)
MsgType 消息类型,此时固定为:text
Content 文本消息内容
MsgId 消息id,64位整型
AgentID 企业应用的id,整型。可在应用的设置页面查看


image消息


   
   
   1348831860
   
   
   
   1234567890123456
   1

参数 说明
ToUserName 企业号CorpID
FromUserName 成员UserID
CreateTime 消息创建时间(整型)
MsgType 消息类型,此时固定为:image
PicUrl 图片链接
MediaId 图片媒体文件id,可以调用获取媒体文件接口拉取数据
MsgId 消息id,64位整型
AgentID 企业应用的id,整型。可在应用的设置页面查看


voice消息


   
   
   1357290913
   
   
   
   1234567890123456
   1

参数 说明
ToUserName 企业号CorpID
FromUserName 成员UserID
CreateTime 消息创建时间(整型)
MsgType 消息类型,此时固定为:voice
MediaId 语音媒体文件id,可以调用获取媒体文件接口拉取数据
Format 语音格式,如amr,speex等
MsgId 消息id,64位整型
AgentID 企业应用的id,整型。可在应用的设置页面查看


video消息


   
   
   1357290913
   
   
   
   1234567890123456
   1

参数 说明
ToUserName 企业号CorpID
FromUserName 成员UserID
CreateTime 消息创建时间(整型)
MsgType 消息类型,此时固定为:video
MediaId 视频媒体文件id,可以调用获取媒体文件接口拉取数据
ThumbMediaId 视频消息缩略图的媒体id,可以调用获取媒体文件接口拉取数据
MsgId 消息id,64位整型
AgentID 企业应用的id,整型。可在应用的设置页面查看


location消息


   
   
   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)

响应的消息分为以下几种类型:

text消息


   
    
   1348831860
   
   

参数 说明
ToUserName 成员UserID
FromUserName 企业号CorpID
CreateTime 消息创建时间(整型)
MsgType 消息类型,此时固定为:text
Content 文本消息内容


image消息


   
   
   1348831860
   
   
       
   

参数 说明
ToUserName 成员UserID
FromUserName 企业号CorpID
CreateTime 消息创建时间(整型)
MsgType 消息类型,此时固定为:image
MediaId 图片文件id,可以调用上传媒体文件接口获取


voice消息


   
   
   1357290913
   
   
       
   

参数 说明
ToUserName 成员UserID
FromUserName 企业号CorpID
CreateTime 消息创建时间(整型)
MsgType 消息类型,此时固定为:voice
MediaId 语音文件id,可以调用上传媒体文件接口获取


video消息


   
   
   1357290913
   
   

参数 说明
ToUserName 成员UserID
FromUserName 企业号CorpID
CreateTime 消息创建时间(整型)
MsgType 消息类型,此时固定为:video
MediaId 视频文件id,可以调用上传媒体文件接口获取
Title 视频消息的标题
Description 视频消息的描述


news消息


   
   
   12345678
   
   2
   
       
           <![CDATA[title1]]> 
           
           
           
       
       
           <![CDATA[title]]>
           
           
           
       
   

参数 说明
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();
		}
	}
}

以上代码的关键部分是CoreService类的processQiyeRequest方法,负责处理接收到的消息并获取响应的消息,代码如下:

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;
	}
}

这里面还需要一个工具类MessageUtil,负责处理xml转换成对象或者对象转成xml,代码如下:

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);    
	}
}

以上代码用到了dom4j,所以还要在项目中引入dom4j的jar包。

最后把class文件提交到百度BAE中,发布,重启,这时候在客户端输入任何文本内容都会收到服务器的自动回复。

你可能感兴趣的:(微信平台,微信,公众平台,企业号,消息接收响应回复)