/blog/tuposky-429
前面两章已经介绍了如何接入微信公众平台,这一章说说消息的接收和发送
可以先了解公众平台的消息api接口(接收消息,发送消息)
接收消息
当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
接收的消息类型有6种,分别为:
可以根据官方的api提供的字段建立对应的实体类
如:文本消息
有很多属性是所有消息类型都需要的,可以把这些信息提取出来建立一个基类
Java代码
packagecom.ifp.weixin.entity.Message.req;
/**
*消息基类(用户->公众帐号)
*
*/
publicclassBaseMessage{
/**
*开发者微信号
*/
privateStringToUserName;
/**
*发送方帐号(一个OpenID)
*/
privateStringFromUserName;
/**
*消息创建时间(整型)
*/
privatelongCreateTime;
/**
*消息类型text、image、location、link
*/
privateStringMsgType;
/**
*消息id,64位整型
*/
privatelongMsgId;
publicStringgetToUserName(){
returnToUserName;
}
publicvoidsetToUserName(StringtoUserName){
ToUserName=toUserName;
}
publicStringgetFromUserName(){
returnFromUserName;
}
publicvoidsetFromUserName(StringfromUserName){
FromUserName=fromUserName;
}
publiclonggetCreateTime(){
returnCreateTime;
}
publicvoidsetCreateTime(longcreateTime){
CreateTime=createTime;
}
publicStringgetMsgType(){
returnMsgType;
}
publicvoidsetMsgType(StringmsgType){
MsgType=msgType;
}
publiclonggetMsgId(){
returnMsgId;
}
publicvoidsetMsgId(longmsgId){
MsgId=msgId;
}
}
接收的文本消息
Java代码
packagecom.ifp.weixin.entity.Message.req;
/**
*文本消息
*/
publicclassTextMessageextendsBaseMessage{
/**
*回复的消息内容
*/
privateStringContent;
publicStringgetContent(){
returnContent;
}
publicvoidsetContent(Stringcontent){
Content=content;
}
}
接收的图片消息
Java代码
packagecom.ifp.weixin.entity.Message.req;
publicclassImageMessageextendsBaseMessage{
privateStringpicUrl;
publicStringgetPicUrl(){
returnpicUrl;
}
publicvoidsetPicUrl(StringpicUrl){
this.picUrl=picUrl;
}
}
接收的链接消息
Java代码
packagecom.ifp.weixin.entity.Message.req;
publicclassLinkMessageextendsBaseMessage{
/**
*消息标题
*/
privateStringTitle;
/**
*消息描述
*/
privateStringDescription;
/**
*消息链接
*/
privateStringUrl;
publicStringgetTitle(){
returnTitle;
}
publicvoidsetTitle(Stringtitle){
Title=title;
}
publicStringgetDescription(){
returnDescription;
}
publicvoidsetDescription(Stringdescription){
Description=description;
}
publicStringgetUrl(){
returnUrl;
}
publicvoidsetUrl(Stringurl){
Url=url;
}
}
接收的语音消息
Java代码
packagecom.ifp.weixin.entity.Message.req;
/**
*语音消息
*
*@authorCaspar
*
*/
publicclassVoiceMessageextendsBaseMessage{
/**
*媒体ID
*/
privateStringMediaId;
/**
*语音格式
*/
privateStringFormat;
publicStringgetMediaId(){
returnMediaId;
}
publicvoidsetMediaId(StringmediaId){
MediaId=mediaId;
}
publicStringgetFormat(){
returnFormat;
}
publicvoidsetFormat(Stringformat){
Format=format;
}
}
接收的地理位置消息
Java代码
packagecom.ifp.weixin.entity.Message.req;
/**
*位置消息
*
*@authorcaspar
*
*/
publicclassLocationMessageextendsBaseMessage{
/**
*地理位置维度
*/
privateStringLocation_X;
/**
*地理位置经度
*/
privateStringLocation_Y;
/**
*地图缩放大小
*/
privateStringScale;
/**
*地理位置信息
*/
privateStringLabel;
publicStringgetLocation_X(){
returnLocation_X;
}
publicvoidsetLocation_X(Stringlocation_X){
Location_X=location_X;
}
publicStringgetLocation_Y(){
returnLocation_Y;
}
publicvoidsetLocation_Y(Stringlocation_Y){
Location_Y=location_Y;
}
publicStringgetScale(){
returnScale;
}
publicvoidsetScale(Stringscale){
Scale=scale;
}
publicStringgetLabel(){
returnLabel;
}
publicvoidsetLabel(Stringlabel){
Label=label;
}
}
发送被动响应消息
对于每一个POST请求,开发者在响应包(Get)中返回特定XML结构,对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。请注意,回复图片等多媒体消息时需要预先上传多媒体文件到微信服务器,只支持认证服务号。
同样,建立响应消息的对应实体类
也把公共的属性提取出来,封装成基类
响应消息的基类
Java代码
packagecom.ifp.weixin.entity.Message.resp;
/**
*消息基类(公众帐号->用户)
*/
publicclassBaseMessage{
/**
*接收方帐号(收到的OpenID)
*/
privateStringToUserName;
/**
*开发者微信号
*/
privateStringFromUserName;
/**
*消息创建时间(整型)
*/
privatelongCreateTime;
/**
*消息类型
*/
privateStringMsgType;
/**
*位0x0001被标志时,星标刚收到的消息
*/
privateintFuncFlag;
publicStringgetToUserName(){
returnToUserName;
}
publicvoidsetToUserName(StringtoUserName){
ToUserName=toUserName;
}
publicStringgetFromUserName(){
returnFromUserName;
}
publicvoidsetFromUserName(StringfromUserName){
FromUserName=fromUserName;
}
publiclonggetCreateTime(){
returnCreateTime;
}
publicvoidsetCreateTime(longcreateTime){
CreateTime=createTime;
}
publicStringgetMsgType(){
returnMsgType;
}
publicvoidsetMsgType(StringmsgType){
MsgType=msgType;
}
publicintgetFuncFlag(){
returnFuncFlag;
}
publicvoidsetFuncFlag(intfuncFlag){
FuncFlag=funcFlag;
}
}
响应文本消息
Java代码
packagecom.ifp.weixin.entity.Message.resp;
/**
*文本消息
*/
publicclassTextMessageextendsBaseMessage{
/**
*回复的消息内容
*/
privateStringContent;
publicStringgetContent(){
returnContent;
}
publicvoidsetContent(Stringcontent){
Content=content;
}
}
响应图文消息
Java代码
packagecom.ifp.weixin.entity.Message.resp;
importjava.util.List;
/**
*多图文消息,
*单图文的时候Articles只放一个就行了
*@authorCaspar.chen
*/
publicclassNewsMessageextendsBaseMessage{
/**
*图文消息个数,限制为10条以内
*/
privateintArticleCount;
/**
*多条图文消息信息,默认第一个item为大图
*/
privateListArticles;
publicintgetArticleCount(){
returnArticleCount;
}
publicvoidsetArticleCount(intarticleCount){
ArticleCount=articleCount;
}
publicListgetArticles(){
returnArticles;
}
publicvoidsetArticles(Listarticles){
Articles=articles;
}
}
图文消息的定义
Java代码
packagecom.ifp.weixin.entity.Message.resp;
/**
*图文消息
*
*/
publicclassArticle{
/**
*图文消息名称
*/
privateStringTitle;
/**
*图文消息描述
*/
privateStringDescription;
/**
*图片链接,支持JPG、PNG格式,
*较好的效果为大图640*320,小图80*80
*/
privateStringPicUrl;
/**
*点击图文消息跳转链接
*/
privateStringUrl;
publicStringgetTitle(){
returnTitle;
}
publicvoidsetTitle(Stringtitle){
Title=title;
}
publicStringgetDescription(){
returnnull==Description?"":Description;
}
publicvoidsetDescription(Stringdescription){
Description=description;
}
publicStringgetPicUrl(){
returnnull==PicUrl?"":PicUrl;
}
publicvoidsetPicUrl(StringpicUrl){
PicUrl=picUrl;
}
publicStringgetUrl(){
returnnull==Url?"":Url;
}
publicvoidsetUrl(Stringurl){
Url=url;
}
}
响应音乐消息
Java代码
packagecom.ifp.weixin.entity.Message.resp;
/**
*音乐消息
*/
publicclassMusicMessageextendsBaseMessage{
/**
*音乐
*/
privateMusicMusic;
publicMusicgetMusic(){
returnMusic;
}
publicvoidsetMusic(Musicmusic){
Music=music;
}
}
音乐消息的定义
Java代码
packagecom.ifp.weixin.entity.Message.resp;
/**
*音乐消息
*/
publicclassMusic{
/**
*音乐名称
*/
privateStringTitle;
/**
*音乐描述
*/
privateStringDescription;
/**
*音乐链接
*/
privateStringMusicUrl;
/**
*高质量音乐链接,WIFI环境优先使用该链接播放音乐
*/
privateStringHQMusicUrl;
publicStringgetTitle(){
returnTitle;
}
publicvoidsetTitle(Stringtitle){
Title=title;
}
publicStringgetDescription(){
returnDescription;
}
publicvoidsetDescription(Stringdescription){
Description=description;
}
publicStringgetMusicUrl(){
returnMusicUrl;
}
publicvoidsetMusicUrl(StringmusicUrl){
MusicUrl=musicUrl;
}
publicStringgetHQMusicUrl(){
returnHQMusicUrl;
}
publicvoidsetHQMusicUrl(StringmusicUrl){
HQMusicUrl=musicUrl;
}
}
构建好之后的项目结构图为
到这里,请求消息和响应消息的实体类都定义好了
解析请求消息
用户向微信公众平台发送消息后,微信公众平台会通过post请求发送给我们。
上一章中WeixinController 类的post方法我们空着
现在我们要在这里处理用户请求了。
因为微信的发送和接收都是用xml格式的,所以我们需要处理请求过来的xml格式。
发送的时候也需要转化成xml格式再发送给微信,所以封装了消息处理的工具类,用到dome4j和xstream两个jar包
Java代码
packagecom.ifp.weixin.util;
importjava.io.InputStream;
importjava.io.Writer;
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;
importjavax.servlet.http.HttpServletRequest;
importorg.dom4j.Document;
importorg.dom4j.Element;
importorg.dom4j.io.SAXReader;
importcom.ifp.weixin.entity.Message.resp.Article;
importcom.ifp.weixin.entity.Message.resp.MusicMessage;
importcom.ifp.weixin.entity.Message.resp.NewsMessage;
importcom.ifp.weixin.entity.Message.resp.TextMessage;
importcom.thoughtworks.xstream.XStream;
importcom.thoughtworks.xstream.core.util.QuickWriter;
importcom.thoughtworks.xstream.io.HierarchicalStreamWriter;
importcom.thoughtworks.xstream.io.xml.PrettyPrintWriter;
importcom.thoughtworks.xstream.io.xml.XppDriver;
/**
*消息工具类
*
*/
publicclassMessageUtil{
/**
*解析微信发来的请求(XML)
*
*@paramrequest
*@return
*@throwsException
*/
publicstaticMapparseXml(HttpServletRequestrequest)throwsException{
//将解析结果存储在HashMap中
Mapmap=newHashMap();
//从request中取得输入流
InputStreaminputStream=request.getInputStream();
//读取输入流
SAXReaderreader=newSAXReader();
Documentdocument=reader.read(inputStream);
//得到xml根元素
Elementroot=document.getRootElement();
//得到根元素的所有子节点
@SuppressWarnings("unchecked")
ListelementList=root.elements();
//遍历所有子节点
for(Elemente:elementList)
map.put(e.getName(),e.getText());
//释放资源
inputStream.close();
inputStream=null;
returnmap;
}
/**
*文本消息对象转换成xml
*
*@paramtextMessage文本消息对象
*@returnxml
*/
publicstaticStringtextMessageToXml(TextMessagetextMessage){
xstream.alias("xml",textMessage.getClass());
returnxstream.toXML(textMessage);
}
/**
*音乐消息对象转换成xml
*
*@parammusicMessage音乐消息对象
*@returnxml
*/
publicstaticStringmusicMessageToXml(MusicMessagemusicMessage){
xstream.alias("xml",musicMessage.getClass());
returnxstream.toXML(musicMessage);
}
/**
*图文消息对象转换成xml
*
*@paramnewsMessage图文消息对象
*@returnxml
*/
publicstaticStringnewsMessageToXml(NewsMessagenewsMessage){
xstream.alias("xml",newsMessage.getClass());
xstream.alias("item",newArticle().getClass());
returnxstream.toXML(newsMessage);
}
/**
*扩展xstream,使其支持CDATA块
*
*/
privatestaticXStreamxstream=newXStream(newXppDriver(){
publicHierarchicalStreamWritercreateWriter(Writerout){
returnnewPrettyPrintWriter(out){
//对所有xml节点的转换都增加CDATA标记
booleancdata=true;
protectedvoidwriteText(QuickWriterwriter,Stringtext){
if(cdata){
writer.write("
writer.write(text);
writer.write("]]>");
}else{
writer.write(text);
}
}
};
}
});
}
接下来在处理业务逻辑,建立一个接收并响应消息的service类,并针对用户输入的1或2回复不同的信息给用户
Java代码
packagecom.ifp.weixin.biz.core.impl;
importjava.util.Date;
importjava.util.Map;
importjavax.servlet.http.HttpServletRequest;
importorg.apache.log4j.Logger;
importorg.springframework.stereotype.Service;
importcom.ifp.weixin.biz.core.CoreService;
importcom.ifp.weixin.constant.Constant;
importcom.ifp.weixin.entity.Message.resp.TextMessage;
importcom.ifp.weixin.util.MessageUtil;
@Service("coreService")
publicclassCoreServiceImplimplementsCoreService{
publicstaticLoggerlog=Logger.getLogger(CoreServiceImpl.class);
@Override
publicStringprocessRequest(HttpServletRequestrequest){
StringrespMessage=null;
try{
//xml请求解析
MaprequestMap=MessageUtil.parseXml(request);
//发送方帐号(open_id)
StringfromUserName=requestMap.get("FromUserName");
//公众帐号
StringtoUserName=requestMap.get("ToUserName");
//消息类型
StringmsgType=requestMap.get("MsgType");
TextMessagetextMessage=newTextMessage();
textMessage.setToUserName(fromUserName);
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(newDate().getTime());
textMessage.setMsgType(Constant.RESP_MESSAGE_TYPE_TEXT);
textMessage.setFuncFlag(0);
//文本消息
if(msgType.equals(Constant.REQ_MESSAGE_TYPE_TEXT)){
//接收用户发送的文本消息内容
Stringcontent=requestMap.get("Content");
if("1".equals(content)){
textMessage.setContent("1是很好的");
//将文本消息对象转换成xml字符串
respMessage=MessageUtil.textMessageToXml(textMessage);
}elseif("2".equals(content)){
textMessage.setContent("我不是2货");
//将文本消息对象转换成xml字符串
respMessage=MessageUtil.textMessageToXml(textMessage);
}
}
}catch(Exceptione){
e.printStackTrace();
}
returnrespMessage;
}
}
接下来在controller里面的post方法里面调用即可
WeixinController类的完整代码
Java代码
packagecom.ifp.weixin.controller;
importjava.io.IOException;
importjava.io.PrintWriter;
importjava.io.UnsupportedEncodingException;
importjavax.annotation.Resource;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.springframework.stereotype.Controller;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestMethod;
importcom.ifp.weixin.biz.core.CoreService;
importcom.ifp.weixin.util.SignUtil;
@Controller
@RequestMapping("/weixinCore")
publicclassWeixinController{
@Resource(name="coreService")
privateCoreServicecoreService;
@RequestMapping(method=RequestMethod.GET)
publicvoidget(HttpServletRequestrequest,HttpServletResponseresponse){
//微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
Stringsignature=request.getParameter("signature");
//时间戳
Stringtimestamp=request.getParameter("timestamp");
//随机数
Stringnonce=request.getParameter("nonce");
//随机字符串
Stringechostr=request.getParameter("echostr");
PrintWriterout=null;
try{
out=response.getWriter();
//通过检验signature对请求进行校验,若校验成功则原样返回echostr,否则接入失败
if(SignUtil.checkSignature(signature,timestamp,nonce)){
out.print(echostr);
}
}catch(IOExceptione){
e.printStackTrace();
}finally{
out.close();
out=null;
}
}
@RequestMapping(method=RequestMethod.POST)
publicvoidpost(HttpServletRequestrequest,HttpServletResponseresponse){
try{
request.setCharacterEncoding("UTF-8");
}catch(UnsupportedEncodingExceptione){
e.printStackTrace();
}
response.setCharacterEncoding("UTF-8");
//调用核心业务类接收消息、处理消息
StringrespMessage=coreService.processRequest(request);
//响应消息
PrintWriterout=null;
try{
out=response.getWriter();
out.print(respMessage);
}catch(IOExceptione){
e.printStackTrace();
}finally{
out.close();
out=null;
}
}
}
效果如下:
ok,大功告成,消息的接收和发送就写完了。