对柳峰博主的微信公众号后台示例的部分重构
Posted eXcel
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对柳峰博主的微信公众号后台示例的部分重构相关的知识,希望对你有一定的参考价值。
柳峰博主的专栏(http://blog.csdn.net/column/details/wechatmp.html)和王信平博主的专栏(http://www.cnblogs.com/wangshuo1/)对微信公众号开发已经做了比较详尽的阐述,基本上照搬,就可以做出第一个微信公众号后台应用。但是在『照搬』的过程中,发现有些地方总是觉得别扭,由着完美主义者的性格使然,对以下这几个地方做个小优化吧。
一、区别消息和响应
原来的消息类(用户发给后台)和响应类(后台回给用户)有这些:
我改成了这样的:
我把『消息Message』和『响应Response』在命名上分开了。这样在编程时不会总是想着要去区别,这个『Message』究竟是用户发给后台的『消息』呢,还是后台回给用户的『响应』。
二、给消息和响应添加构造函数
原来消息和响应都只是使用缺省构造函数(详见http://blog.csdn.net/lyq8479/article/details/8949088),实际上是把这两个类当做了『结构体』来看待。基于面向对象的思想,还是觉得需要有构造函数。
2.1 BaseMessage类的构造函数
1 public BaseMessage(String toUserName, String fromUserName, String createTime, String msgType, String msgId){ 2 this.ToUserName=toUserName; 3 this.FromUserName=fromUserName; 4 this.CreateTime=Long.parseLong(createTime); 5 this.MsgType=msgType; 6 this.MsgId=Long.parseLong(msgId); 7 } 8 9 public BaseMessage(Map<String, String> mapMessage){ 10 try { 11 // 开发者微信号 12 String toUserName = mapMessage.get("ToUserName"); 13 // 发送方帐号(open_id) 14 String fromUserName = mapMessage.get("FromUserName"); 15 // 消息类型 16 String msgType = mapMessage.get("MsgType"); 17 // 建立时间 18 String createTime = mapMessage.get("CreateTime"); 19 // 消息ID 20 String msgId = mapMessage.get("MsgId"); 21 22 this.ToUserName = toUserName; 23 this.FromUserName = fromUserName; 24 this.CreateTime = Long.parseLong(createTime); 25 this.MsgType = msgType; 26 this.MsgId = Long.parseLong(msgId); 27 } 28 catch (Exception e) { 29 e.printStackTrace(); 30 } 31 }
2.2 ImageMessage类的构造函数
1 public ImageMessage(String toUserName, 2 String fromUserName, 3 String createTime, 4 String msgType, 5 String msgId, 6 String picUrl) { 7 super(toUserName,fromUserName,createTime,msgType,msgId); 8 assert (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)); 9 this.PicUrl=picUrl; 10 } 11 12 public ImageMessage(Map<String, String> mapMessage){ 13 super(mapMessage); 14 assert (this.getMsgType().equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)); 15 this.PicUrl=mapMessage.get("PicUrl"); 16 }
2.3 TextMessage类的构造函数
1 public TextMessage(String toUserName, 2 String fromUserName, 3 String createTime, 4 String msgType, 5 String msgId, 6 String content) { 7 super(toUserName,fromUserName,createTime,msgType,msgId); 8 assert (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)); 9 this.Content=content; 10 11 } 12 13 public TextMessage(Map<String, String> mapMessage){ 14 super(mapMessage); 15 assert (this.getMsgType().equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)); 16 this.Content=mapMessage.get("Content"); 17 }
这里只列举了消息类的基类、图片消息、文本消息三个构造函数,其余的位置消息、声音消息、短视频、视频、链接消息的构造函数类似。
2.4 BaseResponse类的构造函数(这个有点意思)
1 public BaseResponse(BaseMessage baseMessage){ 2 this.ToUserName=baseMessage.getFromUserName(); //返回的目标是当初的发送源 3 this.FromUserName=baseMessage.getToUserName(); //发送的来源是当初的发送目标 4 this.MsgType= MessageUtil.RESP_MESSAGE_TYPE_TEXT; // 默认返回消息为文本 5 this.CreateTime=new Date().getTime(); //默认为当前时间 6 this.FuncFlag=0; //默认为非星标 7 } 8 9 public BaseResponse(String toUserName,String fromUserName, String msgType){ 10 this.ToUserName=toUserName; 11 this.FromUserName=fromUserName; 12 this.MsgType=msgType; 13 this.CreateTime=new Date().getTime(); //默认为当前时间 14 this.FuncFlag=0; //默认为非星标 15 }
响应类的基类的构造函数,我使用了消息类的基类作为参数,是因为响应消息的返回目标,就是当初的发送源(用户),响应消息的发送源,其实就是当初的发送目标(微信后台程序)。
2.5 TextResponse类的构造函数
1 public TextResponse(String toUserName,String fromUserName, String msgType,String content){ 2 super(toUserName,fromUserName,msgType); 3 this.Content=content; 4 } 5 public TextResponse(BaseMessage baseMessage, 6 String content){ 7 super(baseMessage); 8 this.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); 9 this.Content=content; 10 }
2.6 Article类的构造函数
1 public Article(String title, String description, String picUrl, String url) { 2 this.Title=title; 3 this.Description=description; 4 this.PicUrl=picUrl; 5 this.Url=url; 6 }
2.7 NewsResponse类的构造函数
1 public class NewsResponse extends BaseResponse { 2 // 图文消息个数,限制为10条以内 3 private int ArticleCount; 4 // 多条图文消息信息,默认第一个item为大图 5 private List<Article> Articles; 6 7 public NewsResponse(String toUserName,String fromUserName, String msgType){ 8 super(toUserName,fromUserName,msgType); 9 ArticleCount=0; 10 this.Articles = new ArrayList<Article>(); 11 } 12 public NewsResponse(BaseMessage baseMessage){ 13 super(baseMessage); 14 this.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_NEWS); 15 ArticleCount=0; 16 this.Articles = new ArrayList<Article>(); 17 } 18 19 //如果已经达到10篇文章,图文消息不可以再增加新的文章。返回FALSE; 20 public boolean addArticle(Article article) { 21 if (ArticleCount>=10){ 22 return false; 23 } 24 Articles.add(article); 25 ArticleCount++; 26 return true; 27 } 28 public Article getArticle(int articleIndex){ 29 return Articles.get(articleIndex); 30 } 31 32 public int getArticleCount() { 33 return ArticleCount; 34 } 35 36 // public void setArticleCount(int articleCount) { 37 // ArticleCount = articleCount; 38 // } 39 40 public List<Article> getArticles() { 41 return Articles; 42 } 43 44 // public void setArticles(List<Article> articles) { 45 // Articles = articles; 46 // } 47 }
这个NewsResponse类也要说一下,原来的设计也够奇葩的,居然直接把List结构暴露出来对其读写,List里的Article的数量,可以单独赋值,呵呵。于是果断注释掉计数变量的读写方法,新增了addArticle和getArticle这两个方法,用以从List中添加和获取Article,计算变量,应该自动累加,不应该对其手工赋值。
MusicResponse类的改造类似,不细说了。
三、归一化命名
原来的响应消息转成XML有三个函数,分别是textMessageToXml、musicMessageToXml、newsMessageToXml(详见http://blog.csdn.net/lyq8479/article/details/8949088),我把它们的命名归一为responseToXml,调用的时候,也不用区别传进去的是啥响应消息来选择不同名字的函数,省心。
1 /** 2 * 文本消息对象转换成xml 3 * 4 * @param textMessage 文本消息对象 5 * @return xml 6 */ 7 public static String responseToXml(TextResponse textMessage) { 8 xstream.alias("xml", textMessage.getClass()); 9 return xstream.toXML(textMessage); 10 } 11 12 /** 13 * 音乐消息对象转换成xml 14 * 15 * @param musicMessage 音乐消息对象 16 * @return xml 17 */ 18 public static String responseToXml(MusicResponse musicMessage) { 19 xstream.alias("xml", musicMessage.getClass()); 20 return xstream.toXML(musicMessage); 21 } 22 23 /** 24 * 图文消息对象转换成xml 25 * 26 * @param newsMessage 图文消息对象 27 * @return xml 28 */ 29 public static String responseToXml(NewsResponse newsMessage) { 30 xstream.alias("xml", newsMessage.getClass()); 31 xstream.alias("item", newsMessage.getArticle(0).getClass()); 32 return xstream.toXML(newsMessage); 33 }
四、构造消息路由
原来的消息处理的主函数(详见http://blog.csdn.net/lyq8479/article/details/9393195),在一个超级巨大的processRequest函数中对消息进行处理,一堆if else if 下来,眼都花了,所以我简化了processRequest函数,只在这个函数里面构造消息路由,针对不同类型的消息,转去不同的处理逻辑中。
1 //定义一个logger,用于发送日志,可以在console或者服务器上看到。存储目录在log4j.properties中定义 2 private static final Logger logger = Logger.getLogger(CoreService.class); 3 4 //处理请求 5 public static String processRequest(HttpServletRequest request){ 6 String returnMsg=null; 7 try{ 8 Map<String, String> requestMap = MessageUtil.parseXml(request); //解析XML 9 String msgType = requestMap.get("MsgType"); //消息类型 10 logger.info("***step1 msgType=" + msgType); 11 switch (msgType){ 12 case MessageUtil.REQ_MESSAGE_TYPE_TEXT: 13 returnMsg=HandleTextMsg(new TextMessage(requestMap)); 14 break; 15 case MessageUtil.REQ_MESSAGE_TYPE_IMAGE: 16 returnMsg=HandleImageMsg(new ImageMessage(requestMap)); 17 break; 18 case MessageUtil.REQ_MESSAGE_TYPE_LINK: 19 returnMsg=HandleLinkMsg(new LinkMessage(requestMap)); 20 break; 21 case MessageUtil.REQ_MESSAGE_TYPE_LOCATION: 22 returnMsg=HandleLocationMsg(new LocationMessage(requestMap)); 23 break; 24 case MessageUtil.REQ_MESSAGE_TYPE_SHORT_VIDEO: 25 returnMsg=HandleShortVideoMsg(new ShortVideoMessage(requestMap)); 26 break; 27 case MessageUtil.REQ_MESSAGE_TYPE_VIDEO: 28 returnMsg=HandleVideoMsg(new VideoMessage(requestMap)); 29 break; 30 case MessageUtil.REQ_MESSAGE_TYPE_VOICE: 31 returnMsg=HandleVoiceMsg(new VoiceMessage(requestMap)); 32 break; 33 case MessageUtil.REQ_MESSAGE_TYPE_EVENT: 34 // 事件类型 35 String eventType = requestMap.get("Event"); 36 switch (eventType){ 37 case MessageUtil.EVENT_TYPE_SUBSCRIBE: 38 returnMsg=HandleSubscribeEvent(new BaseMessage(requestMap)); 39 break; 40 case MessageUtil.EVENT_TYPE_UNSUBSCRIBE: 41 returnMsg=HandleUnsubscribeEvent(new BaseMessage(requestMap)); 42 break; 43 case MessageUtil.EVENT_TYPE_CLICK: 44 returnMsg=HandleClickEvent(new BaseMessage(requestMap)); 45 break; 46 default: 47 returnMsg=HandleDefaultMsgOrEvent(new BaseMessage(requestMap)); 48 } 49 break; 50 default: 51 returnMsg=HandleDefaultMsgOrEvent(new BaseMessage(requestMap)); 52 } 53 }catch (Exception e) { 54 e.printStackTrace(); 55 } 56 return returnMsg; 57 }
下面是对各种消息的handle函数
1 public static String HandleTextMsg(TextMessage textMessage) 2 { 3 String respContent = "您发送的是文本消息!"; 4 if (textMessage.getContent().indexOf("/:") > -1) 5 respContent="你"+textMessage.getContent()+"什么?"; 6 TextResponse textResponse=new TextResponse(textMessage, respContent); 7 String respMsg=MessageUtil.responseToXml(textResponse); 8 logger.info("***step2 Response=" + respMsg); 9 return respMsg; 10 } 11 public static String HandleImageMsg(ImageMessage imageMessage) 12 { 13 String title="鉴定结果"; 14 String picUrl=imageMessage.getPicUrl(); 15 String forwardUrl= HttpTools.imagePlusPlusPath+picUrl; 16 logger.info("***step2 image++ Url=" + forwardUrl); 17 18 //从image++网站识别上传的图片的拍摄地点、拍摄物体。 19 String imagePlusPlusReturn=getHttpResponse(forwardUrl); 20 logger.info("***step3 image++ Response=" + imagePlusPlusReturn); 21 String description = ReadJSONString(imagePlusPlusReturn); 22 logger.info("***step4 image++ Response=" + description); 23 24 Article article=new Article(title,description,picUrl,forwardUrl); 25 NewsResponse newsResponse=new NewsResponse(imageMessage); 26 newsResponse.addArticle(article); 27 String respMsg=MessageUtil.responseToXml(newsResponse); 28 logger.info("***step5 Weixin Response=" + respMsg); 29 return respMsg; 30 } 31 public static String HandleLinkMsg(LinkMessage linkMessage) 32 { 33 String respContent = "您发送的是链接!"; 34 TextResponse textResponse=new TextResponse(linkMessage, respContent); 35 String respMsg=MessageUtil.responseToXml(textResponse); 36 logger.info("***step2 Response=" + respMsg); 37 return respMsg; 38 } 39 public static String HandleLocationMsg(LocationMessage locationMessage) 40 { 41 String respContent = "您发送的是地理位置!"; 42 TextResponse textResponse=new TextResponse(locationMessage, respContent); 43 String respMsg=MessageUtil.responseToXml(textResponse); 44 logger.info("***step2 Response=" + respMsg); 45 return respMsg; 46 } 47 public static String HandleShortVideoMsg(ShortVideoMessage shortVideoMessage) 48 { 49 String respContent = "您发送的是小视频!"; 50 TextResponse textResponse=new TextResponse(shortVideoMessage, respContent); 51 String respMsg=MessageUtil.responseToXml(textResponse); 52 logger.info("***step2 Response=" + respMsg); 53 return respMsg; 54 } 55 public static String HandleVideoMsg(VideoMessage videoMessage) 56 { 57 String respContent = "您发送的是视频!"; 58 TextResponse textResponse=new TextResponse(videoMessage, respContent); 59 String respMsg=MessageUtil.responseToXml(textResponse); 60 logger.info("***step2 Response=" + respMsg); 61 return respMsg; 62 } 63 public static String HandleVoiceMsg(VoiceMessage voiceMessage) 64 { 65 String respContent = "您发送的是语音!"; 66 TextResponse textResponse=new TextResponse(voiceMessage, respContent); 67 String respMsg=MessageUtil.responseToXml(textResponse); 68 logger.info("***step2 Response=" + respMsg); 69 return respMsg; 70 } 71 public static String HandleSubscribeEvent(BaseMessage baseMessage) 72 { 73 String respContent = "欢迎订阅!"; 74 TextResponse textResponse=new TextResponse(baseMessage, respContent); 75 String respMsg=MessageUtil.responseToXml(textResponse); 76 logger.info("***step2 Response=" + respMsg); 77 return respMsg; 78 } 79 public static String HandleUnsubscribeEvent(BaseMessage baseMessage) 80 { 81 String respContent = "再见,希望您再来!"; 82 TextResponse textResponse=new TextResponse(baseMessage, respContent); 83 String respMsg=MessageUtil.responseToXml(textResponse); 84 logger.info("***step2 Response=" + respMsg); 85 return respMsg; 86 } 87 public static String HandleClickEvent(BaseMessage baseMessage) 88 { 89 String respContent = "您点击了自定义菜单!"; 90 TextResponse textResponse=new TextResponse(baseMessage, respContent); 91 String respMsg=MessageUtil.responseToXml(textResponse); 92 logger.info("***step2 Response=" + respMsg); 93 return respMsg; 94 } 95 public static String HandleDefaultMsgOrEvent(BaseMessage baseMessage) 96 { 97 String respContent = "请求不可识别,请稍候尝试!"; 98 TextResponse textResponse=new TextResponse(baseMessage, respContent); 99 String respMsg=MessageUtil.responseToXml(textResponse); 100 logger.info("***step2 Response=" + respMsg); 101 return respMsg; 102 }
五、应用:试试图像识别
我的第一个微信公众号后台应用,是识别用户上传的图片,是什么东西,在哪里拍摄的。调用的是image++网站的api,核心代码如下:
1 /* 2 读取一个网址的所有返回内容 3 */ 4 public static String getHttpResponse(String connectUrl) { 5 BufferedReader in = null; 6以上是关于对柳峰博主的微信公众号后台示例的部分重构的主要内容,如果未能解决你的问题,请参考以下文章