微信公衆平臺開發教程Java版(三) 消息接收和發送

前面兩章已經介紹瞭如何接入微信公衆平臺,這一章說說消息的接收和發送

可以先了解公衆平臺的消息api接口(接收消息,發送消息)

http://mp.weixin.qq.com/wiki/index.php


 

接收消息

當普通微信用戶向公衆賬號發消息時,微信服務器將POST消息的XML數據包到開發者填寫的URL上。

 

 http://mp.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

 

接收的消息類型有6種,分別爲:

可以根據官方的api提供的字段建立對應的實體類

如:文本消息

 

有很多屬性是所有消息類型都需要的,可以把這些信息提取出來建立一個基類

 

package com.ifp.weixin.entity.Message.req;

/**
 * 消息基類(用戶 -> 公衆帳號)
 * 
 */
public class BaseMessage {
	/**
	 * 開發者微信號
	 */
	private String ToUserName;
	/**
	 * 發送方帳號(一個OpenID)
	 */
	private String FromUserName;
	/**
	 * 消息創建時間 (整型)
	 */
	private long CreateTime;

	/**
	 * 消息類型 text、image、location、link
	 */
	private String MsgType;

	/**
	 * 消息id,64位整型
	 */
	private long MsgId;

	public String getToUserName() {
		return ToUserName;
	}

	public void setToUserName(String toUserName) {
		ToUserName = toUserName;
	}

	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 long getMsgId() {
		return MsgId;
	}

	public void setMsgId(long msgId) {
		MsgId = msgId;
	}

}

 接收的文本消息

 

package com.ifp.weixin.entity.Message.req;

/**
 * 文本消息
 */
public class TextMessage extends BaseMessage {
	/**
	 * 回覆的消息內容
	 */
	private String Content;

	public String getContent() {
		return Content;
	}

	public void setContent(String content) {
		Content = content;
	}
}

 接收的圖片消息

package com.ifp.weixin.entity.Message.req;

public class ImageMessage extends BaseMessage{

	private String picUrl;

	public String getPicUrl() {
		return picUrl;
	}

	public void setPicUrl(String picUrl) {
		this.picUrl = picUrl;
	}
	
}

 

 

接收的鏈接消息

package com.ifp.weixin.entity.Message.req;


public class LinkMessage extends BaseMessage {
	/**
	 * 消息標題
	 */
	private String Title;
	/**
	 * 消息描述
	 */
	private String Description;
	/**
	 * 消息鏈接
	 */
	private String Url;

	public String getTitle() {
		return Title;
	}

	public void setTitle(String title) {
		Title = title;
	}

	public String getDescription() {
		return Description;
	}

	public void setDescription(String description) {
		Description = description;
	}

	public String getUrl() {
		return Url;
	}

	public void setUrl(String url) {
		Url = url;
	}

}

 

 接收的語音消息

 

package com.ifp.weixin.entity.Message.req;

/**
 * 語音消息
 * 
 * @author Caspar
 * 
 */
public class VoiceMessage extends BaseMessage {
	/**
	 * 媒體ID
	 */
	private String MediaId;
	/**
	 * 語音格式
	 */
	private String Format;

	public String getMediaId() {
		return MediaId;
	}

	public void setMediaId(String mediaId) {
		MediaId = mediaId;
	}

	public String getFormat() {
		return Format;
	}

	public void setFormat(String format) {
		Format = format;
	}

}

 接收的地理位置消息

 

package com.ifp.weixin.entity.Message.req;


/**
 * 位置消息
 * 
 * @author caspar
 * 
 */
public class LocationMessage extends BaseMessage {
	/**
	 * 地理位置維度
	 */
	private String Location_X;
	/**
	 * 地理位置經度
	 */
	private String Location_Y;

	/**
	 * 地圖縮放大小
	 */
	private String Scale;

	/**
	 * 地理位置信息
	 */
	private String Label;

	public String getLocation_X() {
		return Location_X;
	}

	public void setLocation_X(String location_X) {
		Location_X = location_X;
	}

	public String getLocation_Y() {
		return Location_Y;
	}

	public void setLocation_Y(String location_Y) {
		Location_Y = location_Y;
	}

	public String getScale() {
		return Scale;
	}

	public void setScale(String scale) {
		Scale = scale;
	}

	public String getLabel() {
		return Label;
	}

	public void setLabel(String label) {
		Label = label;
	}

}

 

 

發送被動響應消息

    對於每一個POST請求,開發者在響應包(Get)中返回特定XML結構,對該消息進行響應(現支持回覆文本、圖片、圖文、語音、視頻、音樂)。請注意,回覆圖片等多媒體消息時需要預先上傳多媒體文件到微信服務器,只支持認證服務號。

 

    同樣,建立響應消息的對應實體類

    也把公共的屬性提取出來,封裝成基類

 

     響應消息的基類

package com.ifp.weixin.entity.Message.resp;

/**
 * 消息基類(公衆帳號 -> 用戶)
 */
public class BaseMessage {
	
	/**
	 * 接收方帳號(收到的OpenID)
	 */
	private String ToUserName;
	/**
	 * 開發者微信號
	 */
	private String FromUserName;
	/**
	 * 消息創建時間 (整型)
	 */
	private long CreateTime;
	
	/**
	 * 消息類型
	 */
	private String MsgType;
	
	/**
	 * 位0x0001被標誌時,星標剛收到的消息
	 */
	private int FuncFlag;

	public String getToUserName() {
		return ToUserName;
	}

	public void setToUserName(String toUserName) {
		ToUserName = toUserName;
	}

	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 int getFuncFlag() {
		return FuncFlag;
	}

	public void setFuncFlag(int funcFlag) {
		FuncFlag = funcFlag;
	}
}

 

 

    響應文本消息

   

package com.ifp.weixin.entity.Message.resp;


/**
 * 文本消息
 */
public class TextMessage extends BaseMessage {
	/**
	 * 回覆的消息內容
	 */
	private String Content;

	public String getContent() {
		return Content;
	}

	public void setContent(String content) {
		Content = content;
	}
}

 

 

響應圖文消息

   

package com.ifp.weixin.entity.Message.resp;

import java.util.List;

/**
 * 多圖文消息,
 * 單圖文的時候 Articles 只放一個就行了
 * @author Caspar.chen
 */
public class NewsMessage extends BaseMessage {
	/**
	 * 圖文消息個數,限制爲10條以內
	 */
	private int ArticleCount;
	/**
	 * 多條圖文消息信息,默認第一個item爲大圖
	 */
	private List<Article> Articles;

	public int getArticleCount() {
		return ArticleCount;
	}

	public void setArticleCount(int articleCount) {
		ArticleCount = articleCount;
	}

	public List<Article> getArticles() {
		return Articles;
	}

	public void setArticles(List<Article> articles) {
		Articles = articles;
	}
}

 圖文消息的定義

 

 

package com.ifp.weixin.entity.Message.resp;

/**
 * 圖文消息
 * 
 */
public class Article {
	/**
	 * 圖文消息名稱
	 */
	private String Title;

	/**
	 * 圖文消息描述
	 */
	private String Description;

	/**
	 * 圖片鏈接,支持JPG、PNG格式,<br>
	 * 較好的效果爲大圖640*320,小圖80*80
	 */
	private String PicUrl;

	/**
	 * 點擊圖文消息跳轉鏈接
	 */
	private String Url;

	public String getTitle() {
		return Title;
	}

	public void setTitle(String title) {
		Title = title;
	}

	public String getDescription() {
		return null == Description ? "" : Description;
	}

	public void setDescription(String description) {
		Description = description;
	}

	public String getPicUrl() {
		return null == PicUrl ? "" : PicUrl;
	}

	public void setPicUrl(String picUrl) {
		PicUrl = picUrl;
	}

	public String getUrl() {
		return null == Url ? "" : Url;
	}

	public void setUrl(String url) {
		Url = url;
	}

}

 

 

響應音樂消息

 

package com.ifp.weixin.entity.Message.resp;



/**
 * 音樂消息
 */
public class MusicMessage extends BaseMessage {
	/**
	 * 音樂
	 */
	private Music Music;

	public Music getMusic() {
		return Music;
	}

	public void setMusic(Music music) {
		Music = music;
	}
}

 

 

音樂消息的定義

package com.ifp.weixin.entity.Message.resp;

/**
 * 音樂消息
 */
public class Music {
	/**
	 * 音樂名稱
	 */
	private String Title;
	
	/**
	 * 音樂描述
	 */
	private String Description;
	
	/**
	 * 音樂鏈接
	 */
	private String MusicUrl;
	
	/**
	 * 高質量音樂鏈接,WIFI環境優先使用該鏈接播放音樂
	 */
	private String HQMusicUrl;

	public String getTitle() {
		return Title;
	}

	public void setTitle(String title) {
		Title = title;
	}

	public String getDescription() {
		return Description;
	}

	public void setDescription(String description) {
		Description = description;
	}

	public String getMusicUrl() {
		return MusicUrl;
	}

	public void setMusicUrl(String musicUrl) {
		MusicUrl = musicUrl;
	}

	public String getHQMusicUrl() {
		return HQMusicUrl;
	}

	public void setHQMusicUrl(String musicUrl) {
		HQMusicUrl = musicUrl;
	}

}

 
 構建好之後的項目結構圖爲

 

 

到這裏,請求消息和響應消息的實體類都定義好了

 

解析請求消息

 

用戶向微信公衆平臺發送消息後,微信公衆平臺會通過post請求發送給我們。

上一章中WeixinController 類的post方法我們空着

 

 現在我們要在這裏處理用戶請求了。

 

因爲微信的發送和接收都是用xml格式的,所以我們需要處理請求過來的xml格式。

發送的時候也需要轉化成xml格式再發送給微信,所以封裝了消息處理的工具類,用到dome4j和xstream兩個jar包

package com.ifp.weixin.util;

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.ifp.weixin.entity.Message.resp.Article;
import com.ifp.weixin.entity.Message.resp.MusicMessage;
import com.ifp.weixin.entity.Message.resp.NewsMessage;
import com.ifp.weixin.entity.Message.resp.TextMessage;
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;

/**
 * 消息工具類
 * 
 */
public class MessageUtil {

	/**
	 * 解析微信發來的請求(XML)
	 * 
	 * @param request
	 * @return
	 * @throws Exception
	 */
	public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
		// 將解析結果存儲在HashMap中
		Map<String, String> map = new HashMap<String, String>();

		// 從request中取得輸入流
		InputStream inputStream = request.getInputStream();
		// 讀取輸入流
		SAXReader reader = new SAXReader();
		Document document = reader.read(inputStream);
		// 得到xml根元素
		Element root = document.getRootElement();
		// 得到根元素的所有子節點
		
		@SuppressWarnings("unchecked")
		List<Element> elementList = root.elements();

		// 遍歷所有子節點
		for (Element e : elementList)
			map.put(e.getName(), e.getText());

		// 釋放資源
		inputStream.close();
		inputStream = null;

		return map;
	}

	/**
	 * 文本消息對象轉換成xml
	 * 
	 * @param textMessage 文本消息對象
	 * @return xml
	 */
	public static String textMessageToXml(TextMessage textMessage) {
		xstream.alias("xml", textMessage.getClass());
		return xstream.toXML(textMessage);
	}

	/**
	 * 音樂消息對象轉換成xml
	 * 
	 * @param musicMessage 音樂消息對象
	 * @return xml
	 */
	public static String musicMessageToXml(MusicMessage musicMessage) {
		xstream.alias("xml", musicMessage.getClass());
		return xstream.toXML(musicMessage);
	}

	/**
	 * 圖文消息對象轉換成xml
	 * 
	 * @param newsMessage 圖文消息對象
	 * @return xml
	 */
	public static String newsMessageToXml(NewsMessage newsMessage) {
		xstream.alias("xml", newsMessage.getClass());
		xstream.alias("item", new Article().getClass());
		return xstream.toXML(newsMessage);
	}

	/**
	 * 擴展xstream,使其支持CDATA塊
	 * 
	 */
	private static XStream xstream = new XStream(new XppDriver() {
		public HierarchicalStreamWriter createWriter(Writer out) {
			return new PrettyPrintWriter(out) {
				// 對所有xml節點的轉換都增加CDATA標記
				boolean cdata = true;
				protected void writeText(QuickWriter writer, String text) {
					if (cdata) {
						writer.write("<![CDATA[");
						writer.write(text);
						writer.write("]]>");
					} else {
						writer.write(text);
					}
				}
			};
		}
	});
	
	
}

 接下來在處理業務邏輯,建立一個接收並響應消息的service類,並針對用戶輸入的1或2回覆不同的信息給用戶

 

package com.ifp.weixin.biz.core.impl;

import java.util.Date;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

import com.ifp.weixin.biz.core.CoreService;
import com.ifp.weixin.constant.Constant;
import com.ifp.weixin.entity.Message.resp.TextMessage;
import com.ifp.weixin.util.MessageUtil;

@Service("coreService")
public class CoreServiceImpl implements CoreService{

	public static Logger log = Logger.getLogger(CoreServiceImpl.class);
	
	
	@Override
	public String processRequest(HttpServletRequest request) {
		String respMessage = null;
		try {
			// xml請求解析
			Map<String, String> requestMap = MessageUtil.parseXml(request);

			// 發送方帳號(open_id)
			String fromUserName = requestMap.get("FromUserName");
			// 公衆帳號
			String toUserName = requestMap.get("ToUserName");
			// 消息類型
			String msgType = requestMap.get("MsgType");

			TextMessage textMessage = new TextMessage();
			textMessage.setToUserName(fromUserName);
			textMessage.setFromUserName(toUserName);
			textMessage.setCreateTime(new Date().getTime());
			textMessage.setMsgType(Constant.RESP_MESSAGE_TYPE_TEXT);
			textMessage.setFuncFlag(0);
			// 文本消息
			if (msgType.equals(Constant.REQ_MESSAGE_TYPE_TEXT)) {
				// 接收用戶發送的文本消息內容
				String content = requestMap.get("Content");

				if ("1".equals(content)) {
					textMessage.setContent("1是很好的");
					// 將文本消息對象轉換成xml字符串
					respMessage = MessageUtil.textMessageToXml(textMessage);
				}else if ("2".equals(content)) {
					textMessage.setContent("我不是2貨");
					// 將文本消息對象轉換成xml字符串
					respMessage = MessageUtil.textMessageToXml(textMessage);
				}
			} 
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		return respMessage;
	}


}

 接下來在controller裏面的post方法裏面調用即可

 

WeixinController類的完整代碼

package com.ifp.weixin.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.ifp.weixin.biz.core.CoreService;
import com.ifp.weixin.util.SignUtil;

@Controller
@RequestMapping("/weixinCore")
public class WeixinController {

	@Resource(name="coreService")
	private CoreService coreService;
	
	@RequestMapping(method = RequestMethod.GET)
	public void get(HttpServletRequest request, HttpServletResponse response) {
		// 微信加密簽名,signature結合了開發者填寫的token參數和請求中的timestamp參數、nonce參數。
		String signature = request.getParameter("signature");
		// 時間戳
		String timestamp = request.getParameter("timestamp");
		// 隨機數
		String nonce = request.getParameter("nonce");
		// 隨機字符串
		String echostr = request.getParameter("echostr");

		PrintWriter out = null;
		try {
			out = response.getWriter();
			// 通過檢驗signature對請求進行校驗,若校驗成功則原樣返回echostr,否則接入失敗
			if (SignUtil.checkSignature(signature, timestamp, nonce)) {
				out.print(echostr);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			out.close();
			out = null;
		}
	}

	@RequestMapping(method = RequestMethod.POST)
	public void post(HttpServletRequest request, HttpServletResponse response) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		response.setCharacterEncoding("UTF-8");

		// 調用核心業務類接收消息、處理消息
		String respMessage = coreService.processRequest(request);

		// 響應消息
		PrintWriter out = null;
		try {
			out = response.getWriter();
			out.print(respMessage);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			out.close();
			out = null;
		}
	}

}

 

 效果如下:

 

 ok,大功告成,消息的接收和發送就寫完了。

可以關注我的公衆號一起討論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章