前几天开发了微信公众号,趁今天有时间,总结一下。
接收普通消息要注意几点:
1 . 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。
2 .当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
3 .如果开发者需要对用户消息在5秒内立即做出回应,即使用“发送消息-被动回复消息”接口向用户被动回复消息时,可以在公众平台官网的开发者中心处设置消息加密。开启加密后,用户发来的消息和开发者回复的消息都会被加密(但开发者通过客服接口等API调用形式向用户发送消息,则不受影响)。
被动回复用户消息要注意几点:
1 .当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。严格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。
2 .微信服务器在将用户的消息发给公众号的开发者服务器地址(开发者中心处配置)后,微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次,如果在调试中,发现用户无法收到响应的消息,可以检查是否消息处理超时。关于重试的消息排重,有msgid的消息推荐使用msgid排重。事件类型消息推荐使用FromUserName + CreateTime 排重。
3 .开发者希望增强安全性,可以在开发者中心处开启消息加密,这样,用户发给公众号的消息以及公众号被动回复用户消息都会继续加密(但),详见被动回复消息加解密说明。
4 .假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示。1、直接回复success(推荐方式) 2、直接回复空串(指字节长度为0的空字符串,而不是XML结构体中content字段的内容为空)
注: 发消息注册6种格式,本文只讲述文本格式消息,如果你想找支持6种消息格式的,可以直接退出,去找其他资料。
注意看:
1 .接收普通消息数据格式:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
2 .被动回复用户信息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
以下是代码:
1.工具类,作用: 解析微信请求并且读取XML,我这只是进行学习,没有封装,全部放在一起了。
/**
* 解析微信请求并读取XML
* @param request
* @return
* @throws IOException
* @throws DocumentException
*/
public static Map<String,String> readWeixinXml(HttpServletRequest request) throws IOException, DocumentException {
Map<String,String> map = new HashMap<String,String>();
//获取输入流
InputStream input = request.getInputStream();
//使用dom4j的SAXReader读取(org.dom4j.io.SAXReader;)
SAXReader sax = new SAXReader();
Document doc = sax.read(input);
//获取XML数据包根元素
Element root = doc.getRootElement();
//得到根元素的所有子节点
@SuppressWarnings("unchecked")
List<Element> elementList = root.elements();
//遍历所有节点并将其放进map
for(Element e : elementList){
map.put(e.getName(), e.getText());
}
//释放资源
input.close();
input = null;
return map;
}
2 .Controller层,你也可以写Service层,与实现层Impl
@RequestMapping("/replyTextMessage")
@ResponseBody
public static void replyTextMessage(HttpServletRequest request, HttpServletResponse response){
Map<String,String> map = null;
//从工具类中获取XML解析之后的map
try {
map = WeiXinController.readWeixinXml(request);
} catch (IOException e) {
System.out.println("获取输入流失败");
} catch (DocumentException e) {
System.out.println("读取XML失败");
}
//获取发送方账号
String fromUserName = map.get("FromUserName");
//接收方账号(开发者微信号)
String toUserName = map.get("ToUserName");
//消息类型
String msgType = map.get("MsgType");
//文本内容
String content = map.get("Content");
System.out.println("发送方账号:"+fromUserName+",接收方账号(开发者微信号):"+toUserName+",消息类型:"+msgType+",文本内容:"+content);
//回复消息
if(msgType.equals("text")){
//根据开发文档要求构造XML字符串,本文为了让流程更加清晰,直接拼接
//这里在开发的时候可以优化,将回复的文本内容构造成一个java类
//然后使用XStream(com.thoughtworks.xstream.XStream)将java类转换成XML字符串,后面将会使用这个方法。
//而且由于参数中没有任何特殊字符,为简单起见,没有添加<![CDATA[xxxxx]]>
String replyMsg = "<xml>"+
"<ToUserName>"+fromUserName+"</ToUserName>"+
"<FromUserName>"+toUserName+"</FromUserName>"+
"<CreateTime>"+System.currentTimeMillis()/1000+"</CreateTime>"+
"<MsgType>"+msgType+"</MsgType>"+
"<Content>"+content+"</Content>"+
"</xml>";
//响应消息
System.out.println("响应消息:"+replyMsg);
PrintWriter out = null;
try {
//设置回复内容编码方式为UTF-8,防止乱码
response.setCharacterEncoding("UTF-8");
out = response.getWriter();
//我们这里将用户发送的消息原样返回
out.print(replyMsg);
System.out.println("响应成功");
} catch (IOException e) {
System.out.println("获取输出流失败");
}finally {
if(out != null){
out.close();
out = null;
}
}
}
}
这个实现的是你在公众号发信息,公众号会返回相同的信息,你可以自己进行封装,改动。代码是完整的,希望可以帮助到大家!