一,接收微信消息
上一篇博文是介绍如何进行服务器端配置,配置的相当于为微信的消息接口,微信向服务器端推送消息或者推送用户发送的消息就会推送到该配置的URL中,配置好了过后,我们就需要在服务器端接收用户发送的一系列消息。
服务器端接收用户消息和接收微信推送消息是使用的同一个地址,但是有一个区别接收推送消息为GET方法,接收用户发送的消息为POST方法。
使用流接收POST消息转为String结果为XML的消息体,如果是在微信后台选择的为安全模式则需要解密,若是明文模式,那就直接解析XML就是了,此处为加密模式需用到解密类,本博文使用的为微信提供的解密类。链接为:
微信加解密文档:
http://mp.weixin.qq.com/wiki/11/2d2d1df945b75605e7fea9ea2573b667.html
加解密代码:
http://mp.weixin.qq.com/wiki/static/assets/a5a22f38cb60228cb32ab61d9e4c414b.zip
环境为:SpringMvc4
栗子:
//微信接收消息接口
@ResponseBody
@RequestMapping(value = "signature",method = RequestMethod.POST)
public String signature(HttpServletRequest request) throws AesException{
try {
//接收流
ServletInputStream sis = request.getInputStream();
StringBuilder xmlMsg = new StringBuilder();
byte[] b = new byte[4096];
for (int n; (n = sis.read(b)) != -1;) {
xmlMsg.append(new String(b, 0, n, "UTF-8"));
}
System.out.println(xmlMsg);
//接收加密的xml
Object[] encrypt = XMLParse.extract(xmlMsg.toString());
//解密
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt("token",
"encodingAesKey",
"appid");
//获取解密后的XML
String msg = wxcpt.decrypt(encrypt[1].toString());
//转对象
WxMsgRequest wmr = this.convertWxMesgToObj(msg);
System.out.println(wmr);
if(wmr.getMsgType().equals(WxRequestMsgType.MSG_TEXT)){
WxTextMsgResponse wtmr = new WxTextMsgResponse();
wtmr.setContent("点点与麦兜的TEST");
Long createTime = System.currentTimeMillis() / 1000;
wtmr.setCreateTime(createTime.toString());
wtmr.setFromUserName(wmr.getToUserName());
wtmr.setToUserName(wmr.getFromUserName());
wtmr.setMsgType(WxRequestMsgType.MSG_TEXT);
String returnStr = this.convertObjToXml(wtmr);
return returnStr;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//微信验证接口
@ResponseBody
@RequestMapping(value = "signature",method=RequestMethod.GET)
public String signature(String signature,
String timestamp,
String nonce,
String echostr){
try {
String shaPw = SHA1.getSHA1("xiaofli007", timestamp, nonce);
if(signature.equals(shaPw))
return echostr;
else
return null;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//将xml数据解析为对象
private WxMsgRequest convertWxMesgToObj(String msg) throws JAXBException{
JAXBContext context = JAXBContext.newInstance(WxMsgRequest.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
return (WxMsgRequest)unmarshaller.unmarshal(new StringReader(msg));
}
//将对象转为xml字符串
public String convertObjToXml(Object obj) throws JAXBException{
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
StringWriter writer = new StringWriter();
marshaller.marshal(obj, writer);
String temp = writer.toString();
return temp;
}
解密前消息:
<xml>
<ToUserName><![CDATA[gh_e1c44a3a9f81]]></ToUserName>
<Encrypt><![CDATA[HusAtbuEO8XmYaH4tzuZHcdi7Kg2e6szJfZrzdQLnpEs6D0jrNWKLFYftafLyKLFy7sOPbQUYI8DaVPPV/0npFSRym2oskoN0ip3q93XXtunbz5OBmWlYsvKnJuYK/UlIttgrJbrRFJlz4AKiHYUnJx7vaSAFvErAlhudPA1rhPEa64sARRGRQhrP1m/zffbSRl6uI7Wz1jsIsfiZAPg0nxqfxdDXh9Gklcp6DNHhf3phAx9pt8kyZKh4n8yhHV1+R+WU0c86YEgwQ7NHgA0CaryQPemkNRF06YrWEvE0meToeT08sm2A0bZJUiGGmj8dD9/b6KsESaQmfsF2JzdRvSq79gLO+Mn0swQS4wjOnbrgw7otvlYCZnDu4+xcfv+lGgMIc0YfG/jtT4VH6pboBtm1xCRs01N14SHB8pGryA=]]></Encrypt>
</xml>
解密后的消息:
<xml>
<ToUserName><![CDATA[gh_e1c44a3a9f81]]></ToUserName>
<FromUserName><![CDATA[oMffXwBl4N4F29vbH3UAbrdhMq4Y]]></FromUserName>
<CreateTime>1475129963</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[11]]></Content>
<MsgId>6335634948877288984</MsgId>
</xml>
二,识别消息
1、消息类型:
文本消息(text)
图片消息(image)
语音消息(voice)
视频消息(video)
小视频消息 (shortvideo)
地理位置消息(location)
链接消息(link)
创建一个实体类,用于接受消息(为了方便我是全部字段写到一个类中,也可以先判断类型:再指定接受的类型):
@XmlRootElement(name = "xml")
public class WxMsgRequest {
/*** 开发者微信号 */
private String ToUserName;
/*** 发送方帐号(一个OpenID)*/
private String FromUserName;
/*** 消息创建时间 (整型)*/
private String CreateTime;
/*** 消息类型有:text ; event ; 语音为voice*/
private String MsgType;
/*** 消息id*/
private String MsgId;
/*** 语音消息媒体id,可以调用多媒体文件下载接口拉取数据。*/
private String MediaId;
/*** 语音格式,如amr,speex等*/
private String Format;
/*** 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。*/
private String ThumbMediaId;
/*** 文本消息内容*/
private String Content;
/*** 图片消息URL*/
private String picUrl;
/*** 地理位置消息 地理位置纬度*/
private String gpsX;
/*** 地理位置消息 地理位置经度*/
private String gpsY;
/*** 地理位置消息 地图缩放大小*/
private String scale;
/*** 地理位置消息 地理位置信息*/
private String label;
/*** 链接消息 消息标题*/
private String title;
/*** 链接消息 消息描述*/
private String description;
/*** 链接消息 消息链接*/
private String url;
/**
* 事件类型,subscribe(订阅)、unsubscribe(取消订阅)、CLICK(自定义菜单点击事件)
* WeixinRequestMsgType#SUBSCRIBE_EVENT
*/
private String Event;
/*** 事件KEY值,与自定义菜单接口中KEY值对应*/
private String EventKey;
/** 二维码的ticket,可用来换取二维码图片 **/
private String Ticket;
@XmlElement(name = "Ticket")
public String getTicket() {
return Ticket;
}
public void setTicket(String ticket) {
Ticket = ticket;
}
//由于文章篇幅问题,下面的参数set方法和get方法就不展示了。。每个get方法加@XmlElement(name="参数名")
}
将xml解析成对象:
private WxMsgRequest convertWxMesgToObj(String msg) throws JAXBException{
JAXBContext context = JAXBContext.newInstance(WxMsgRequest.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
return (WxMsgRequest)unmarshaller.unmarshal(new StringReader(msg));
}
三,回复消息
1、消息回复类型:
文本消息(text)
图片消息 ( image )
语音消息 ( voice )
视频消息 ( video )
音乐消息 ( music )
图文消息 ( picurl )
此处栗子为文本消息回复。
创建一个微信消息类型实体,用于判断消息类型:
public class WxRequestMsgType {
/*** 请求的,应答的,文本推送消息 */
public static final String MSG_TEXT = "text";
/**** 请求的, 图片推送消息*/
public static final String MSG_IMAGE = "image";
/*** 请求的, 地理位置信息*/
public static final String MSG_LOCATION = "location";
/*** 请求的, 链接信息*/
public static final String MSG_LINK = "link";
/*** 请求的,事件推送消息*/
public static final String MSG_EVENT = "event";
/*** 应答的,图文消息体*/
public static final String MSG_NEW = "news";
/*** 请求的,语音推送消息*/
public static final String MSG_VOICE = "voice";
/*** 事件类型订阅*/
public static final String EVENT_SUBSCRIBE = "subscribe";
/*** 事件取消订阅*/
public static final String EVENT_UNSUBSCRIBE = "unsubscribe";
/*** 自定义菜单的点击事件*/
public static final String EVENT_CLICK = "CLICK";
public static final String EVENT_SCAN = "SCAN";
/**
* 如果公众号处于开发模式,需要在接收到用户发送的消息时, 返回一个MsgType为transfer_customer_service的消息,
* 微信服务器在收到这条消息时,会把这次发送的消息转到多客服系统。 用户被客服接入以后,客服关闭会话以前,处于会话过程中,
* 用户发送的消息均会被直接转发至客服系统
*/
public static final String TRANSFER_CUSTOMER_SERVICE_MSG = "transfer_customer_service";
}
创建文本回复实体:
@XmlRootElement(name = "xml")
public class WxTextMsgResponse{
/*** 开发者微信号*/
private String ToUserName;
/*** 发送方帐号(一个OpenID)*/
private String FromUserName;
/*** 消息创建时间 (整型)*/
private String CreateTime;
/*** 应答消息类型有: news ;text;*/
private String MsgType;
/*** 文本消息内容*/
private String Content;
/**
* 位0x0001被标志时,星标刚收到的消息。
*/
private String FuncFlag;
@XmlElement(name = "Content")
public String getContent() {
return Content;
}
public void setContent(String content) {
Content = content;
}
//省略其他get、set方法
}
2,判断回复
JAXB2对象转为XML字符串:
public String convertObjToXml(Object obj) throws JAXBException{
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
StringWriter writer = new StringWriter();
marshaller.marshal(obj, writer);
String temp = writer.toString();
return temp;
}
在signature、POST方法中加入文本消息的判断进行回复:
if(wmr.getMsgType().equals(WxRequestMsgType.MSG_TEXT)){
WxTextMsgResponse wtmr = new WxTextMsgResponse();
wtmr.setContent("点点与麦兜的TEST");
Long createTime = System.currentTimeMillis() / 1000;
wtmr.setCreateTime(createTime.toString());
wtmr.setFromUserName(wmr.getToUserName());
wtmr.setToUserName(wmr.getFromUserName());
wtmr.setMsgType(WxRequestMsgType.MSG_TEXT);
String returnStr = this.convertObjToXml(wtmr);
return returnStr;
}
四,场景应用
此处只举例了消息接受和文本消息的回复,后面还有主动推送消息、还有其他事件,比如关注事件,取消关注事件等、这里就不一一举例了,其他事件接受和回复请查看微信开发文档(其实是大同小异)
1,这个回复其实可以做一个后台自动回复管理。比如后台设置关键字回复、被添加回复、自动回复,发送过来的消息进行内容识别,是否为关键字,或者自动回复等。
来源:oschina
链接:https://my.oschina.net/u/2435940/blog/753371