对柳峰博主的微信公众号后台示例的部分重构

柳峰博主的专栏(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         StringBuffer result = null;
 7         try {
 8             URI uri = new URI(connectUrl);
 9             URL url = uri.toURL();
10             URLConnection connection = url.openConnection();
11             connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
12             connection.setRequestProperty("Charset", "utf-8");
13             connection.connect();
14
15             result = new StringBuffer();
16             //读取URL的响应
17             in = new BufferedReader(new InputStreamReader(
18                     connection.getInputStream()));
19             String line;
20             while ((line = in.readLine()) != null) {
21                 result.append(line);
22             }
23
24             return result.toString();
25
26         } catch (Exception e) {
27             e.printStackTrace();
28         }finally {
29             try {
30                 if (in != null) {
31                     in.close();
32                 }
33             } catch (Exception e2) {
34                 e2.printStackTrace();
35             }
36         }
37         return null;
38     }
39
40     /*
41     从JSON字符串中读取某些关键信息
42     JSON文件来自:http://www.imageplusplus.com/doc
43      */
44     public static String ReadJSONString(String jSonString){
45         try {
46             int imageConfidenceIndex=jSonString.indexOf("confidence",jSonString.indexOf("image"));
47             int imageConfidenceIndexEnd=jSonString.indexOf(",",imageConfidenceIndex);
48             String imageConfidence=jSonString.substring(imageConfidenceIndex+12,imageConfidenceIndexEnd);
49             int imageConfidencePercent=(int)(Float.parseFloat(imageConfidence)*100);
50             int imageValueIndex=jSonString.indexOf("value",jSonString.indexOf("image"));
51             int imageValueIndexEnd=jSonString.indexOf("}",imageValueIndex)-1;
52             String imageValue=jSonString.substring(imageValueIndex+8,imageValueIndexEnd).trim();
53
54             int sceneConfidenceIndex=jSonString.indexOf("confidence",jSonString.indexOf("scene"));
55             int sceneConfidenceIndexEnd=jSonString.indexOf(",",sceneConfidenceIndex);
56             String sceneConfidence=jSonString.substring(sceneConfidenceIndex+12,sceneConfidenceIndexEnd);
57             int sceneConfidencePercent=(int)(Float.parseFloat(sceneConfidence)*100);
58             int sceneValueIndex=jSonString.indexOf("value",jSonString.indexOf("scene"));
59             int sceneValueIndexEnd=jSonString.indexOf("}",sceneValueIndex)-1;
60             String sceneValue=jSonString.substring(sceneValueIndex+8,sceneValueIndexEnd).trim();
61
62             String result="这张照片有【"+imageConfidencePercent+"%】的可能是【"+imageValue+"】," +
63                     "有【"+sceneConfidencePercent+"%】的可能是在【"+sceneValue+"】拍摄的。";
64             return result;
65         } catch (Exception e) {
66             e.printStackTrace();
67         }
68         return "服务器秀逗了,请重试!";
69     }
70 }

全部的源代码,我放在了这里:20161013微信公众号后台应用示例src.zip

示例源码中用到几个外部jar lib,去官网下载,然后扔进项目里面就好。

时间: 10-11

对柳峰博主的微信公众号后台示例的部分重构的相关文章

郝萌主的微信公众号上线了

更多游戏源码,请点击我 如果文章对您有所帮助,欢迎关注郝萌主的微信公众号,支持郝萌主的独立游戏工作,更多信息就扫扫我吧^_^ + -------------------------------------------------------- End -------------------------------------------------------- 如果文章对您有所帮助,欢迎关注郝萌主的微信公众号,支持郝萌主的独立游戏工作,更多信息就扫扫我吧^_^ + --------------

微信公众号自动回复示例代码

<?php define("TOKEN", "weixin"); $wechatObj = new wechatCallbackapiTest(); $wechatObj->responseMsg(); class wechatCallbackapiTest { public function responseMsg() { //get post data, May be due to the different environments $postSt

微信公众号退款开发

博主是小菜鸟,这篇文章仅是自己开发的随笔记录,不足博友可以指出来,一起进步 1.[微信支付]公众号支付开发者文档链接地址 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 调用微信退款接口,需要发送特定的xml格式字符串到到微信退款接口: 2.微信申请退款需要双向证书, JAVA只需要使用apiclient_cert.p12即可,证书从 https://pay.weixin.qq.com/index.php/core/hom

微信公众号开发技术基础(二):外网映射工具ngrok的简介和使用

本文结构:(一)用法(二)示例(三)qydev ngrok的下载链接及相关网站 微信公众号开发的时候,微信服务器是需要访问我们的一个公网服务器地址的,但我们又是在本地调试自己的程序的,那怎样让微信服务器能访问到我们本地的服务器呢?那就需要用外网映射工具,将本地IP映射成公网IP,这样就能在公网上访问本地服务了,这里使用ngrok.ngrok原版程序的服务器是在国外的,访问速度极慢或者干脆访问不了,所以这里提供一个服务器搭建在国内的基于ngrok的一个软件:qydev,百度网盘下载链接见文章结尾.

微信公众号支付开发全过程 --JAVA

按照惯例,开头总得写点感想 ------------------------------------------------------------------ 业务流程 这个微信官网说的还是很详细的,还配了图.我还要再说一遍. 用户点击一个支付按钮-->{后台一大推处理}-->用户看到了一个输入密码的界面,包含金额等一些信息-->用户输入密码后出来一个支付成功的页面(这部分流程都是微信自己完成的,我们什么都不用做)-->返回系统自己的页面(总不能让用户一直看着一个支付完成的页面吧

好用不需多说的微信公众号实用技巧,一起来叨叨!

至此军训之际,太阳大大当空照,火热的阳光真是好! 小编的心情也是非常的好,想一想那么多嗮黑了,饿瘦了的小学妹们,终于可以显的我好白好白啦(不是白胖白胖)! 白白瘦瘦的我现在比较容易紧张,一紧张我就...喜欢装逼... 装逼我就想到了微信,想到了微信就忍不住和大家叨叨几个超级牛逼的技巧(⊙o⊙) 牛逼的技巧一:图文封面图的获取 不知道大家是否知道如何获取,如果这个图文的封面图比较的符合你的气质,这个时候你就需要下面这样子做了 首先,在电脑上的浏览器打开文章,右键点击查看源代码 然后,点击[Ctrl

2017-9月微信公众号支付-Java详解

微信支付源代码 在此之前,先C麻瓜藤N遍,MD官方文档一半正确一半错误.言归正传, 微信支付整体流程:微信授权登录商户的公众号--微信支付的公众号配置--统一下单--微信js调起支付页面--输入密码支付--支付成功,异步回调URL处理商户的相应业务 一.业务场景: 先看一下支付的业务场景:用户使用微信登录商户页面,点击支付按钮,调起微信支付,选择付款卡号,输入密码,完成支付,如图: 场景十分简单,不过步骤比较多,稍不注意就掉坑里了. 二.微信公众号支付的配置准备: 1)调用公众号支付,首先你得有

微信公众号可快速创建“门店小程序” 不用开发

“门店小程序”是啥?“门店小程序”是小程序的一项新能力.无需开发,商户就可在微信公众号后台快速创建“门店小程序”.这个小程序类似一张“店铺名片”,可以展示线下门店名称.简介.营业时间.联系方式.地理位置和图片等门店信息,并支持在公众号自定义菜单.图文消息和模板消息等场景中使用. 它长这样: (左侧为“门店小程序”示意图,将来商户可根据需要为门店小程序快速配置卡券.支付功能) “门店小程序”和你们平时用到的小程序不同点在于: 页面标准化——便于用户识别:指向性明确——是一个“门店”:创建简单——公

微信商城开发系列第四篇 不写代码玩转微信公众号

本系列文章属作者原创文章,请尊重作者的劳动成果,转载请注明出处:walkingmanc的专栏 , 谢谢! 同时欢迎大家加入微信商城开发QQ群:364072602,共同探讨进步.  为什么叫不写代码玩转微信公众号呢? 我们大家都知道,微信公众号有两种模式,一种是编辑模式,一种是开发模式.所谓的不写代码玩转微信公众号,其实就是在编辑模式下如何使用微信公众号的意思,呵呵,是不是有种恍然大悟的感觉. 其实,如果你关注的微信公众号比较多的话,你会发现有很多有名的公众号,它们没有菜单,每天都会发布4到5篇文

Mac下进行基于java服务端语言的微信公众号本地js-sdk调试的大致方法

开发微信公众号应用调用js-sdk,需要先在微信公众号后台配置可信域名,之后从微信的入口地址重定向到改域名下的路径后便会返回code,之后可以拿到一系列需要的参数等等.那么本地开发,如果使用的是PHP语言,在本地hosts文件添加一条记录,默认80端口,即可在本地使用微信开发者调试工具进行微信公众号应用的开发调试,但如果使用的java语言,默认8080端口,如果设置为80端口启动,非root权限下tomcat是启动不成功的,如果以root权限启动tomcat或者eclipse又会造成其他一些问题