一,用户与服务器间消息交互流程
用户在手机端向公众账号发送一条消息,并收到回复的消息,这个动作的处理过程是怎样的?
接收消息:用户向公众账号发送消息,此时微信服务器接收到消息,并将消息的xml数据包发送到开发者填写的接口配置URL上;
发送消息:对于每一个POST请求,开发者在响应包(Get)中返回特定XML结构,对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。
交互流程:
(1)用户通过微信客户端向公众账号发送消息,消息首先会被微信服务器接收到;
(2)微信服务器接收到消息后,会根据开发者在接口配置信息中填写的URL,将信息通过HTTP POST方式传递到公众账号服务器;
(3)公众账号服务器接收到消息后,会按照业务逻辑进行相应的处理;
(4)处理完成后,公众账号服务器会将处理结果返回给微信服务器;
(5)微信服务器将公众账号服务器返回的消息通过公众账号发送给用户;
注:在真个消息交互过程中,公众账号服务器需要做:(1)接收微信服务器器发送来的消息;(2)根据指定的业务逻辑对消息进行处理;(3)将处理结果返回给微信服务器。
二,消息的分类
微信服务器与公众账号服务器交互的消息分2类:
(1)接收消息:指用户发送给公众账号的消息
a:接收普通消息:有6种类型:文本消息、图片消息、语音消息、视频消息、地理位置消息、链接消息;
b:接收事件推送:
(2)事件:指用户对公众账号做出某种操作时,微信服务器会将对应的事件推送给公众账号服务器。有6种类型:关注/取消关注事件、扫描带参数二维码事件、上报地理位置事件、自定义菜单事件、点击菜单拉取消息时的事件推送、点击菜单跳转链接时的事件推送;
(3)发送消息:指公众账号回复给用户的消息。有6种类型:文本消息、图片消息、语音消息、视频消息、音乐消息、图文消息;
三,实例
package org.liufeng.course.service;
import javax.servlet.http.HttpServlet;
/**
* 请求处理的核心类
*
* @author liufeng
* @date 2013-09-29
*/
public class CoreServlet extends HttpServlet {
/**
* 请求校验与处理
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*********************企业号。。。。Start。。。。*********************************************************************************/
// 将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 微信加密签名
String msg_signature =request.getParameter("msg_signature");
// 时间戳
String timestamp =request.getParameter("timestamp");
// 随机数
String nonce =request.getParameter("nonce");
//从请求中读取整个post数据
InputStream inputStream =request.getInputStream();
String postData =IOUtils.toString(inputStream, "UTF-8");
System.out.println(postData);
String msg = "";
WXBizMsgCrypt wxcpt = null;
try {
wxcpt = new WXBizMsgCrypt(sToken ,sEncodingAESKey , sCorpID );
//解密消息
msg =wxcpt.DecryptMsg(msg_signature, timestamp, nonce, postData);
} catch (AesException e) {
e.printStackTrace();
}
System.out.println("msg=" +msg);
//调用核心服务类接收处理请求
String respXml = CoreService.processRequest(msg);//调用核心业务类
// 响应消息
PrintWriter out = response.getWriter();
out.print(encryptMsg);
out.close();
}
}
public class CoreService {
/**
*处理微信发来的请求——企业号 韩学敏
*
* @param request
* @return xml
*/
public static String processRequest(String msg) {
//xml格式的消息数据
String respXml = null;
//默认返回的文本消息内容
String respContent = "";
try{
// 调用parseXml方法解析请求消息
Map<String,String> requestMap = MessageUtil.parseXml(msg);
// 发送方帐号
String fromUserId = requestMap.get("FromUserName");
// 公众帐号
String toUserName = requestMap.get("ToUserName");
// 消息类型
String msgType = requestMap.get("MsgType");
//获取企业应用Id
String agentId = requestMap.get("AgentID");
// 获取接口访问凭证
String accessToken =CommonUtil.getAccessToken(toUserName, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx").getAccessToken();
// 回复文本消息
TextMessagetextMessage = new TextMessage();
textMessage.setToUserName(fromUserName);
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(newDate().getTime());
textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
//判断消息类型
if(msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {// 文本消息
respContent = "您发送的是文本消息";
}elseif(msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)){//图片
respContent = "您发送的是文本图片消息";
}else if(msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)){//语音
respContent = "您发送的是语音消息";
}else if(msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)){//地理位置
respContent ="您发送的是地理位置消息";
}else if(msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)){//事件推送
respContent = "您发送的是事件推送消息";
}else if(msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VIDEO)){//视频
respContent ="您发送的是视频消息";
}else if(msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)){//链接
respContent = "您发送的是链接消息";
}
textMessage.setContent(respContent);
// 将文本消息对象转换成xml
respXml= MessageUtil.messageToXml(textMessage);
}catch (Exception e) {
e.printStackTrace();
}
returnrespXml ;
}
}
/**
* 通用工具类
*/
public class CommonUtil {
private static Logger log = LoggerFactory.getLogger(CommonUtil.class);
//获取AccessToken的Http请求 韩学敏 2015-2-25 10:58:36
public final static Stringaccesstoken_url="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid =CORPID&corpsecret=CORPSECRET";
/**
*获取接口访问凭证 企业号 ——韩学敏 2015-2-25 10:59:57
*
*@param corpid 企业Id
*@param corpsecret 凭证密钥
* @return
*/
public static Token getAccessToken(String corpid, String corpsecret) {
Token token = null;
String requestUrl = accesstoken_url.replace("CORPID",corpid).replace("CORPSECRET", corpsecret);
// 发起GET请求获取凭证
JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
if(null != jsonObject) {
try{
token= new Token();
token.setAccessToken(jsonObject.getString("access_token"));
token.setExpiresIn(jsonObject.getInt("expires_in"));
}catch (JSONException e) {
token= null;
// 获取token失败
log.error("获取token失败 errcode:{} errmsg:{}",jsonObject.getInt("errcode"),jsonObject.getString("errmsg"));
}
}
returntoken;
}
}
/**
* 消息处理工具类
*/
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";
/**
* 扩展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("<![CDATA[");
writer.write(text);
writer.write("]]>");
}else {
writer.write(text);
}
}
};
}
});
/**
*文本消息对象转换成xml
*
* @paramtextMessage 文本消息对象
* @return xml
*/
public static String messageToXml(TextMessage textMessage) {
xstream.alias("xml",textMessage.getClass());
return xstream.toXML(textMessage);
}
}
}
/**
* 凭证 实体
*/
public class Token {
// 接口访问凭证
private String accessToken;
// 凭证有效期,单位:秒
private int expiresIn;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken= accessToken;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn= expiresIn;
}
}