welcome to xlongwei.com

欢迎大家一起学习、交流、分享


QQ:9167702333 邮箱:admin@xlongwei.com

微信公众号消息回复


分类 Java   关键字 分享   标签 java   algorithm   发布 hongwei  1464330535502
注意 转载须保留原文链接,译文链接,作者译者等信息。  
微信公众号开发主要是响应文本消息(其他消息类似)、点击事件和关注事件,公众号后台可以配置URL服务器地址,消息和事件都会通知到这个URL地址。

CallbackCcontroller负责响应微信的回调请求。
@ResponseBody
@RequestMapping("weixin.xml")
public Object weixin(final HttpServletRequest request) throws IOException {
String echostr = RequestUtil.getStringParam(request, "echostr", null);
if(!StringUtil.isBlank(echostr)) {//每次公众号后台修改配置,都会回调此接口,这里要求响应echostr,校验签名可选
String signature = RequestUtil.getStringParam(request, "signature", null);
String timestamp = RequestUtil.getStringParam(request, "timestamp", null);
String nonce = RequestUtil.getStringParam(request, "nonce", null);
String token = GlobalConfig.getProperty("weixin.token");
return WeixinUtil.checkSignature(signature, timestamp, nonce, token) ? echostr : "";
}else {
return new Callable<String>() {//异步响应回调请求,并发会好一些
@Override
public String call() throws Exception {
String xml = FileUtil.readString(request.getInputStream(), FileUtil.CharsetNames.UTF_8);
logger.info(xml);//请求正文是xml格式
String appid = GlobalConfig.getProperty("weixin.appId");
String timestamp = RequestUtil.getStringParam(request, "timestamp", null);
String nonce = RequestUtil.getStringParam(request, "nonce", null);//请求参数用于校验
boolean aes = "aes".equals(RequestUtil.getStringParam(request, "encrypt_type", null));
if(aes) {
String msgSignature = RequestUtil.getStringParam(request, "msg_signature", null);
String decrypt = WeixinUtil.decrypt(appid, msgSignature, timestamp, nonce, xml);
logger.info("decrypt: "+decrypt);
xml = decrypt;//如果是安全模式,需要解密消息正文
}
Message msg = Message.fromXML(xml);
msg = messageDispatcher.dispatch(msg);//MessageDispatcher分发处理消息,返回响应消息
xml = msg !=null ? msg.toXML() : "";
logger.info(xml);//打印响应内容
if(aes) {
String encrypt = WeixinUtil.encrypt(appid, xml, timestamp, nonce);
logger.info("encrypt: "+encrypt);
xml = encrypt;//如果是安全模式,需要加密响应消息
}
return xml;
}
};
}
}

MessageDispatcher管理多个MessageHandler,后者提供处理消息的接口,具体实现可以有多个
public interface MessageHandler<M extends Message> {
Class<M> type(); //由于动态获取M的class类型比较麻烦,如果能顺利获取则可以省略这个方法(已解决,见getMessageType)
Message handle(M message);
}
public class MessageDispatcher {
/** 分发消息,返回响应 */
public Message dispatch(Message message) {
List<MessageHandler> list = messageHandlers.get(message.getClass());
if(list!=null && list.size()>0) {
for(MessageHandler handler : list) {
Message handle = handler.handle(message);
if(handle != null) {//具体的handler只需要关心响应内容,这里统一处理基础参数
handle.setFromUserName(message.getToUserName());
handle.setToUserName(message.getFromUserName());
handle.setCreateTime(System.currentTimeMillis()/1000);
handle.setMsgId(message.getMsgId());
return handle;
}
}
}
return null;
}

public static Class<? extends Message> getMessageType(Class<? extends MessageHandler> clazz) {
Class superClazz = clazz.getSuperclass();
while(superClazz!=MessageHandlerAbstract.class && superClazz!=MessageHandlerEventAbstract.class) {
clazz = superClazz;
superClazz = clazz.getSuperclass();
}
//class MessageHandlerTextAbstract extends MessageHandlerAbstract<TextMessage>
Type type = clazz.getGenericSuperclass();//MessageHandlerAbstract<TextMessage>
ParameterizedType ptype = (ParameterizedType)type;
Type[] atype = ptype.getActualTypeArguments();//TextMessage
return (Class)atype[0];
}

protected Map<Class, List<MessageHandler>> messageHandlers = new HashMap<>();
}

MessageHandlerAbstract支持向MessageDispatcher注册自己,MessageHandlerTextAbstract支持文本消息处理,MessageHandlerTextPinyin支持获取汉字拼音,继承关系如下图



拼音接口参考文档:http://api.xlongwei.com