微信开发学习总结(二)——微信开发入门

  上一篇《微信开发学习总结(一)——微信开发环境搭建》我们已经完成了微信开发的准备工作,准备工作完成之后,就要开始步入正题了。

一、微信公众平台的基本原理

  在开始做之前,先简单介绍了微信公众平台的基本原理。

  微信服务器就相当于一个转发服务器,终端(手机、Pad等)发起请求至微信服务器,微信服务器然后将请求转发给我们的应用服务器。应用服务器处理完毕后,将响应数据回发给微信服务器,微信服务器再将具体响应信息回复到微信App终端。

  通信协议为:HTTP

  数据传输格式为:XML

  具体的流程如下图所示:

  

  来一张更加直观的图吧:

  

  我们需要做的事情,就是对微信服务器转发的HTTP请求做出响应。具体的请求内容,我们按照特定的XML格式去解析,处理完毕后,也要按照特定的XML格式返回。

二、微信公众号接入

  在微信公众平台开发者文档上,关于公众号接入这一节内容在接入指南上写的比较详细的,文档中说接入公众号需要3个步骤,分别是:

  1、填写服务器配置
  2、验证服务器地址的有效性
  3、依据接口文档实现业务逻辑

  其实,第3步已经不能算做公众号接入的步骤,而是接入之后,开发人员可以根据微信公众号提供的接口所能做的一些开发。

  第1步中服务器配置包含服务器地址(URL)、Token和EncodingAESKey。

  服务器地址即公众号后台提供业务逻辑的入口地址,目前只支持80端口,之后包括接入验证以及任何其它的操作的请求(例如消息的发送、菜单管理、素材管理等)都要从这个地址进入。接入验证和其它请求的区别就是,接入验证时是get请求,其它时候是post请求;

  Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性);

  EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。本例中全部以未加密的明文消息方式,不涉及此配置项。

  第2步,验证服务器地址的有效性,当点击“提交”按钮后,微信服务器将发送一个http的get请求到刚刚填写的服务器地址,并且携带四个参数:

  

  接到请求后,我们需要做如下三步,若确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效,否则接入失败。

  1. 将token、timestamp、nonce三个参数进行字典序排序
  2. 将三个参数字符串拼接成一个字符串进行sha1加密
  3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

  下面我们用Java代码来演示一下这个验证过程

  使用IDE(Eclipse或者IntelliJ IDEA)创建一个JavaWeb项目,这里我使用的是IntelliJ IDEA,项目目录结构如下图所示:

  

  编写一个servlevt,在其中的doGet方法中定义校验方法,具体代码如下:

  1 package me.gacl.wx.web.servlet;
  2
  3 import javax.servlet.ServletException;
  4 import javax.servlet.annotation.WebServlet;
  5 import javax.servlet.http.HttpServlet;
  6 import javax.servlet.http.HttpServletRequest;
  7 import javax.servlet.http.HttpServletResponse;
  8 import java.io.IOException;
  9 import java.security.MessageDigest;
 10 import java.security.NoSuchAlgorithmException;
 11 import java.util.Arrays;
 12
 13 /**
 14  * Created by xdp on 2016/1/25.
 15  * 使用@WebServlet注解配置WxServlet,urlPatterns属性指明了WxServlet的访问路径
 16  */
 17 @WebServlet(urlPatterns="/WxServlet")
 18 public class WxServlet extends HttpServlet {
 19
 20     /**
 21      * Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)
 22      * 比如这里我将Token设置为gacl
 23      */
 24     private final String TOKEN = "gacl";
 25
 26     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 27
 28     }
 29
 30     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 31         System.out.println("开始校验签名");
 32         /**
 33          * 接收微信服务器发送请求时传递过来的4个参数
 34          */
 35         String signature = request.getParameter("signature");//微信加密签名signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
 36         String timestamp = request.getParameter("timestamp");//时间戳
 37         String nonce = request.getParameter("nonce");//随机数
 38         String echostr = request.getParameter("echostr");//随机字符串
 39         //排序
 40         String sortString = sort(TOKEN, timestamp, nonce);
 41         //加密
 42         String mySignature = sha1(sortString);
 43         //校验签名
 44         if (mySignature != null && mySignature != "" && mySignature.equals(signature)) {
 45             System.out.println("签名校验通过。");
 46             //如果检验成功输出echostr,微信服务器接收到此输出,才会确认检验完成。
 47             //response.getWriter().println(echostr);
 48             response.getWriter().write(echostr);
 49         } else {
 50             System.out.println("签名校验失败.");
 51         }
 52
 53     }
 54
 55     /**
 56      * 排序方法
 57      *
 58      * @param token
 59      * @param timestamp
 60      * @param nonce
 61      * @return
 62      */
 63     public String sort(String token, String timestamp, String nonce) {
 64         String[] strArray = {token, timestamp, nonce};
 65         Arrays.sort(strArray);
 66         StringBuilder sb = new StringBuilder();
 67         for (String str : strArray) {
 68             sb.append(str);
 69         }
 70
 71         return sb.toString();
 72     }
 73
 74     /**
 75      * 将字符串进行sha1加密
 76      *
 77      * @param str 需要加密的字符串
 78      * @return 加密后的内容
 79      */
 80     public String sha1(String str) {
 81         try {
 82             MessageDigest digest = MessageDigest.getInstance("SHA-1");
 83             digest.update(str.getBytes());
 84             byte messageDigest[] = digest.digest();
 85             // Create Hex String
 86             StringBuffer hexString = new StringBuffer();
 87             // 字节数组转换为 十六进制 数
 88             for (int i = 0; i < messageDigest.length; i++) {
 89                 String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
 90                 if (shaHex.length() < 2) {
 91                     hexString.append(0);
 92                 }
 93                 hexString.append(shaHex);
 94             }
 95             return hexString.toString();
 96
 97         } catch (NoSuchAlgorithmException e) {
 98             e.printStackTrace();
 99         }
100         return "";
101     }
102 }

  我这里用的Servlet3.0,使用Servlet3.0的好处就是可以直接使用@WebServlet注解映射Servlet的访问路径,不再需要在web.xml文件中进行配置.

  将WxStudy项目部署到Tomcat服务器中运行,直接启动项目,然后用ngrok将本地8080端口映射到外网(如何使用ngrok请参考博客《微信开发学习总结(一)——微信开发环境搭建》)。如下图所示:

  

  测试是否可以通过http://xdp.ngrok.natapp.cn地址正常访问,测试结果如下:

  

  可以看到,我们的项目已经可以被外网正常访问到了。

  进入微信测试公众号管理界面,在接口配置信息中填入映射的外网地址和token,如下图所示:

 

  点击提交按钮,页面会提示配置成功,

  

  IDE的控制台中输出了校验通过的信息,如下图所示:

  

  到此,我们的公众号应用已经能够和微信服务器正常通信了,也就是说我们的公众号已经接入到微信公众平台了。

三、access_token管理

3.1、access_token介绍

  我们的公众号和微信服务器对接成功之后,接下来要做的就是根据我们的业务需求调用微信公众号提供的接口来实现相应的逻辑了。在使用微信公众号接口中都需要一个access_token。

  关于access_token,在微信公众平台开发者文档上的获取接口调用凭据有比较详细的介绍:access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token,开发者需要妥善保存access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。并且每天调用获取access_token接口的上限是2000次。

  总结以上说明,access_token需要做到以下两点:

  1.因为access_token有2个小时的时效性,要有一个机制保证最长2个小时重新获取一次。

  2.因为接口调用上限每天2000次,所以不能调用太频繁。

3.2、微信公众平台提供的获取access_token的接口

  关于access_token的获取方式,在微信公众平台开发者文档上有说明,公众号可以调用一个叫"获取access token"的接口来获取access_token。

  获取access token接口调用请求说明

    http请求方式: GET

    请求的URL地址:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
    
  

  我们可以看到,调用过程中需要传递appID和AppSecret,appID和AppSecret是在申请公众号的时候自动分配给公众号的,相当于公众号的身份标示,使用微信公众号的注册帐号登录到腾讯提供的微信公众号管理后台就可以看到自己申请的公众号的AppID和AppSecret,如下图所示:

  

  这是我申请公众号测试帐号时分配到的AppID和AppSecret。

3.3、获取access_token方案以及具体实现

  这里采用的方案是这样的,定义一个默认启动的servlet,在init方法中启动一个Thread,这个进程中定义一个无限循环的方法,用来获取access_token,当获取成功后,此进程休眠7000秒(7000秒=1.944444444444444小时),否则休眠3秒钟继续获取。流程图如下:

  

  下面正式开始在工程中实现以上思路,因为返回的数据都是json格式,这里会用到阿里的fastjson库,为构造请求和处理请求后的数据序列化和反序列化提供支持。

  1.定义一个AccessToken实体类

 1 package me.gacl.wx.entry;
 2
 3 /**
 4  * AccessToken的数据模型
 5  * Created by xdp on 2016/1/25.
 6  */
 7 public class AccessToken {
 8
 9     //获取到的凭证
10     private String accessToken;
11     //凭证有效时间,单位:秒
12     private int expiresin;
13
14     public String getAccessToken() {
15         return accessToken;
16     }
17
18     public void setAccessToken(String accessToken) {
19         this.accessToken = accessToken;
20     }
21
22     public int getExpiresin() {
23         return expiresin;
24     }
25
26     public void setExpiresin(int expiresin) {
27         this.expiresin = expiresin;
28     }
29 }

 2.定义一个AccessTokenInfo类,用于存放获取到的AccessToken,代码如下:

 1 package me.gacl.wx.Common;
 2
 3 import me.gacl.wx.entry.AccessToken;
 4
 5 /**
 6  * Created by xdp on 2016/1/25.
 7  */
 8 public class AccessTokenInfo {
 9
10     //注意是静态的
11     public static AccessToken accessToken = null;
12 }

  3.编写一个用于发起https请求的工具类NetWorkHelper,代码如下:

 1 package me.gacl.wx.util;
 2
 3 import javax.net.ssl.*;
 4 import java.io.BufferedReader;
 5 import java.io.InputStream;
 6 import java.io.InputStreamReader;
 7 import java.net.URL;
 8 import java.security.cert.CertificateException;
 9 import java.security.cert.X509Certificate;
10
11 /**
12  * 访问网络用到的工具类
13  */
14 public class NetWorkHelper {
15
16     /**
17      * 发起Https请求
18      * @param reqUrl 请求的URL地址
19      * @param requestMethod
20      * @return 响应后的字符串
21      */
22     public String getHttpsResponse(String reqUrl, String requestMethod) {
23         URL url;
24         InputStream is;
25         String resultData = "";
26         try {
27             url = new URL(reqUrl);
28             HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
29             TrustManager[] tm = {xtm};
30
31             SSLContext ctx = SSLContext.getInstance("TLS");
32             ctx.init(null, tm, null);
33
34             con.setSSLSocketFactory(ctx.getSocketFactory());
35             con.setHostnameVerifier(new HostnameVerifier() {
36                 @Override
37                 public boolean verify(String arg0, SSLSession arg1) {
38                     return true;
39                 }
40             });
41
42
43             con.setDoInput(true); //允许输入流,即允许下载
44
45             //在android中必须将此项设置为false
46             con.setDoOutput(false); //允许输出流,即允许上传
47             con.setUseCaches(false); //不使用缓冲
48             if (null != requestMethod && !requestMethod.equals("")) {
49                 con.setRequestMethod(requestMethod); //使用指定的方式
50             } else {
51                 con.setRequestMethod("GET"); //使用get请求
52             }
53             is = con.getInputStream();   //获取输入流,此时才真正建立链接
54             InputStreamReader isr = new InputStreamReader(is);
55             BufferedReader bufferReader = new BufferedReader(isr);
56             String inputLine;
57             while ((inputLine = bufferReader.readLine()) != null) {
58                 resultData += inputLine + "\n";
59             }
60             System.out.println(resultData);
61
62         } catch (Exception e) {
63             e.printStackTrace();
64         }
65         return resultData;
66     }
67
68     X509TrustManager xtm = new X509TrustManager() {
69         @Override
70         public X509Certificate[] getAcceptedIssuers() {
71             return null;
72         }
73
74         @Override
75         public void checkServerTrusted(X509Certificate[] arg0, String arg1)
76                 throws CertificateException {
77
78         }
79
80         @Override
81         public void checkClientTrusted(X509Certificate[] arg0, String arg1)
82                 throws CertificateException {
83
84         }
85     };
86 }

  getHttpsResponse方法是请求一个https地址,参数requestMethod为字符串“GET”或者“POST”,传null或者“”默认为get方式。

  4.定义一个默认启动的servlet,在init方法中启动一个新的线程去获取accessToken

 1 package me.gacl.wx.web.servlet;
 2
 3 import com.alibaba.fastjson.JSON;
 4 import com.alibaba.fastjson.JSONObject;
 5 import me.gacl.wx.Common.AccessTokenInfo;
 6 import me.gacl.wx.entry.AccessToken;
 7 import me.gacl.wx.util.NetWorkHelper;
 8
 9 import javax.servlet.ServletException;
10 import javax.servlet.annotation.WebInitParam;
11 import javax.servlet.annotation.WebServlet;
12 import javax.servlet.http.HttpServlet;
13
14 /**
15  * 用于获取accessToken的Servlet
16  * Created by xdp on 2016/1/25.
17  */
18 @WebServlet(
19         name = "AccessTokenServlet",
20         urlPatterns = {"/AccessTokenServlet"},
21         loadOnStartup = 1,
22         initParams = {
23                 @WebInitParam(name = "appId", value = "wxbe4d433e857e8bb1"),
24                 @WebInitParam(name = "appSecret", value = "ccbc82d560876711027b3d43a6f2ebda")
25         })
26 public class AccessTokenServlet extends HttpServlet {
27
28     @Override
29     public void init() throws ServletException {
30         System.out.println("启动WebServlet");
31         super.init();
32
33         final String appId = getInitParameter("appId");
34         final String appSecret = getInitParameter("appSecret");
35
36         //开启一个新的线程
37         new Thread(new Runnable() {
38             @Override
39             public void run() {
40                 while (true) {
41                     try {
42                         //获取accessToken
43                         AccessTokenInfo.accessToken = getAccessToken(appId, appSecret);
44                         //获取成功
45                         if (AccessTokenInfo.accessToken != null) {
46                             //获取到access_token 休眠7000秒,大约2个小时左右
47                             Thread.sleep(7000 * 1000);
48                             //Thread.sleep(10 * 1000);//10秒钟获取一次
49                         } else {
50                             //获取失败
51                             Thread.sleep(1000 * 3); //获取的access_token为空 休眠3秒
52                         }
53                     } catch (Exception e) {
54                         System.out.println("发生异常:" + e.getMessage());
55                         e.printStackTrace();
56                         try {
57                             Thread.sleep(1000 * 10); //发生异常休眠1秒
58                         } catch (Exception e1) {
59
60                         }
61                     }
62                 }
63
64             }
65         }).start();
66     }
67
68     /**
69      * 获取access_token
70      *
71      * @return AccessToken
72      */
73     private AccessToken getAccessToken(String appId, String appSecret) {
74         NetWorkHelper netHelper = new NetWorkHelper();
75         /**
76          * 接口地址为https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET,其中grant_type固定写为client_credential即可。
77          */
78         String Url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, appSecret);
79         //此请求为https的get请求,返回的数据格式为{"access_token":"ACCESS_TOKEN","expires_in":7200}
80         String result = netHelper.getHttpsResponse(Url, "");
81         System.out.println("获取到的access_token="+result);
82         //使用FastJson将Json字符串解析成Json对象
83         JSONObject json = JSON.parseObject(result);
84         AccessToken token = new AccessToken();
85         token.setAccessToken(json.getString("access_token"));
86         token.setExpiresin(json.getInteger("expires_in"));
87         return token;
88     }
89 }

  AccessTokenServlet采用注解的方式进行配置
  至此代码实现完毕,将项目部署,看到控制台输出如下:

  

  为了方便看效果,可以把休眠时间设置短一点,比如10秒获取一次,然后将access_token输出。

  下面做一个测试jsp页面,并把休眠时间设置为10秒,这样过10秒刷新页面,就可以看到变化

 1 <%-- Created by IntelliJ IDEA. --%>
 2 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 3 <%@ page import="me.gacl.wx.Common.AccessTokenInfo"%>
 4 <html>
 5   <head>
 6     <title></title>
 7   </head>
 8   <body>
 9     微信学习
10     <hr/>
11     access_token为:<%=AccessTokenInfo.accessToken.getAccessToken()%>
12   </body>
13 </html>

  

  10秒钟后刷新页面,access_token变了,如下图所示:

  

四、接收微信服务器发送的消息并做出响应

  经过上述的三步,我们开发前的准备工作已经完成了,接下来要做的就是接收微信服务器发送的消息并做出响应

  从微信公众平台接口消息指南中可以了解到,当用户向公众帐号发消息时,微信服务器会将消息通过POST方式提交给我们在接口配置信息中填写的URL,而我们就需要在URL所指向的请求处理类WxServlet的doPost方法中接收消息、处理消息和响应消息。

4.1.编写一个用于处理消息的工具类

  编写处理消息的工具栏,工具类代码如下:

  1 package me.gacl.wx.util;
  2
  3 import org.dom4j.Document;
  4 import org.dom4j.Element;
  5 import org.dom4j.io.SAXReader;
  6
  7 import javax.servlet.http.HttpServletRequest;
  8 import java.io.InputStream;
  9 import java.text.DateFormat;
 10 import java.text.SimpleDateFormat;
 11 import java.util.Date;
 12 import java.util.HashMap;
 13 import java.util.List;
 14 import java.util.Map;
 15
 16 /**
 17  * 消息处理工具类
 18  * Created by xdp on 2016/1/26.
 19  */
 20 public class MessageHandlerUtil {
 21
 22     /**
 23      * 解析微信发来的请求(XML)
 24      * @param request
 25      * @return map
 26      * @throws Exception
 27      */
 28     public static Map<String,String> parseXml(HttpServletRequest request) throws Exception {
 29         // 将解析结果存储在HashMap中
 30         Map<String,String> map = new HashMap();
 31         // 从request中取得输入流
 32         InputStream inputStream = request.getInputStream();
 33         System.out.println("获取输入流");
 34         // 读取输入流
 35         SAXReader reader = new SAXReader();
 36         Document document = reader.read(inputStream);
 37         // 得到xml根元素
 38         Element root = document.getRootElement();
 39         // 得到根元素的所有子节点
 40         List<Element> elementList = root.elements();
 41
 42         // 遍历所有子节点
 43         for (Element e : elementList) {
 44             System.out.println(e.getName() + "|" + e.getText());
 45             map.put(e.getName(), e.getText());
 46         }
 47
 48         // 释放资源
 49         inputStream.close();
 50         inputStream = null;
 51         return map;
 52     }
 53
 54     // 根据消息类型 构造返回消息
 55     public static String buildXml(Map<String,String> map) {
 56         String result;
 57         String msgType = map.get("MsgType").toString();
 58         System.out.println("MsgType:" + msgType);
 59         if(msgType.toUpperCase().equals("TEXT")){
 60             result = buildTextMessage(map, "孤傲苍狼在学习和总结微信开发了,构建一条文本消息:Hello World!");
 61         }else{
 62             String fromUserName = map.get("FromUserName");
 63             // 开发者微信号
 64             String toUserName = map.get("ToUserName");
 65             result = String
 66                     .format(
 67                             "<xml>" +
 68                                     "<ToUserName><![CDATA[%s]]></ToUserName>" +
 69                                     "<FromUserName><![CDATA[%s]]></FromUserName>" +
 70                                     "<CreateTime>%s</CreateTime>" +
 71                                     "<MsgType><![CDATA[text]]></MsgType>" +
 72                                     "<Content><![CDATA[%s]]></Content>" +
 73                                     "</xml>",
 74                             fromUserName, toUserName, getUtcTime(),
 75                             "请回复如下关键词:\n文本\n图片\n语音\n视频\n音乐\n图文");
 76         }
 77
 78         return result;
 79     }
 80
 81     /**
 82      * 构造文本消息
 83      *
 84      * @param map
 85      * @param content
 86      * @return
 87      */
 88     private static String buildTextMessage(Map<String,String> map, String content) {
 89         //发送方帐号
 90         String fromUserName = map.get("FromUserName");
 91         // 开发者微信号
 92         String toUserName = map.get("ToUserName");
 93         /**
 94          * 文本消息XML数据格式
 95          * <xml>
 96              <ToUserName><![CDATA[toUser]]></ToUserName>
 97              <FromUserName><![CDATA[fromUser]]></FromUserName>
 98              <CreateTime>1348831860</CreateTime>
 99              <MsgType><![CDATA[text]]></MsgType>
100              <Content><![CDATA[this is a test]]></Content>
101              <MsgId>1234567890123456</MsgId>
102          </xml>
103          */
104         return String.format(
105                 "<xml>" +
106                         "<ToUserName><![CDATA[%s]]></ToUserName>" +
107                         "<FromUserName><![CDATA[%s]]></FromUserName>" +
108                         "<CreateTime>%s</CreateTime>" +
109                         "<MsgType><![CDATA[text]]></MsgType>" +
110                         "<Content><![CDATA[%s]]></Content>" + "</xml>",
111                 fromUserName, toUserName, getUtcTime(), content);
112     }
113
114     private static String getUtcTime() {
115         Date dt = new Date();// 如果不需要格式,可直接用dt,dt就是当前系统时间
116         DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");// 设置显示格式
117         String nowTime = df.format(dt);
118         long dd = (long) 0;
119         try {
120             dd = df.parse(nowTime).getTime();
121         } catch (Exception e) {
122
123         }
124         return String.valueOf(dd);
125     }
126 }

  为了方便解析微信服务器发送给我们的xml格式的数据,这里我们借助于开源框架dom4j去解析xml(这里使用的是dom4j-2.0.0-RC1.jar)

  

4.2.在WxServlet的doPost方法中处理请求

  WxServlet的doPost方法的代码如下:

 1  /**
 2      * 处理微信服务器发来的消息
 3      */
 4     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 5         // TODO 接收、处理、响应由微信服务器转发的用户发送给公众帐号的消息
 6         // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
 7         request.setCharacterEncoding("UTF-8");
 8         response.setCharacterEncoding("UTF-8");
 9         System.out.println("请求进入");
10         String result = "";
11         try {
12             Map<String,String> map = MessageHandlerUtil.parseXml(request);
13             System.out.println("开始构造消息");
14             result = MessageHandlerUtil.buildXml(map);
15             System.out.println(result);
16             if(result.equals("")){
17                 result = "未正确响应";
18             }
19         } catch (Exception e) {
20             e.printStackTrace();
21             System.out.println("发生异常:"+ e.getMessage());
22         }
23         response.getWriter().println(result);
24     }

  到此,我们的WxServlet已经可以正常处理用户的请求并做出响应了.接下来我们测试一下我们开发好的公众号应用是否可以正常和微信用户交互

  将WxStudy部署到Tomcat服务器,启动服务器,记得使用ngrok将本地Tomcat服务器的8080端口映射到外网,保证接口配置信息的URL地址:http://xdp.ngrok.natapp.cn/WxServlet可以正常与微信服务器通信

  登录到我们的测试公众号的管理后台,然后用微信扫描一下测试号的二维码,如下图所示:

  

  

  

  关注成功后,我们开发好的公众号应用会先给用户发一条提示用户操作的文本消息,微信用户根据提示操作输入"文本",我们的公众号应用接收到用户请求后就给用户回复了一条我们自己构建好的文本消息,如下图所示:

  

  我们的公众号应用响应给微信用户的文本消息的XML数据如下:

1 <xml>
2   <ToUserName><![CDATA[ojADgs0eDaqh7XkTM9GvDmdYPoDw]]></ToUserName>
3   <FromUserName><![CDATA[gh_43df3882c452]]></FromUserName>
4   <CreateTime>1453755900000</CreateTime>
5   <MsgType><![CDATA[text]]></MsgType>
6   <Content><![CDATA[孤傲苍狼在学习和总结微信开发了,构建一条文本消息:Hello World!]]></Content>
7 </xml>

  测试公众号的管理后台也可以看到关注测试号的用户列表,如下图所示:

  

  通过这个简单的入门程序,我们揭开了微信开发的神秘面纱了.

  本篇的内容就这么多了。本文的内容和代码参考了用java开发微信公众号:公众号接入和access_token管理(二)这篇博客,在此对作者"风的姿态"表示感谢。

  下载测试项目部署运行时,由于项目中使用的是Servlet3.0,所以要求部署的Tomcat必须是7.x,以前也写过几篇关于Servlet3.0的博客,大家有兴趣的话可以去看看,本篇博文对应的测试项目代码下载

时间: 2024-10-05 20:03:05

微信开发学习总结(二)——微信开发入门的相关文章

Python学习(二):入门篇:python中流程控制与函数编写

python中流程控制与函数编写 Last Eidt 2014/5/2 转载请注明出处http://blog.csdn.net/jxlijunhao 一,流程控制 1)布尔逻辑 Python中利用True来表示逻辑真,False来逻辑假 not :非 and:与 or   :或 ==  :逻辑等 >>> False==True False >>> False==False True >>> not False True >>> Fal

Android流媒体开发之路二:NDK开发Android端RTMP直播推流程序

NDK开发Android端RTMP直播推流程序 经过一番折腾,成功把RTMP直播推流代码,通过NDK交叉编译的方式,移植到了Android下,从而实现了Android端采集摄像头和麦克缝数据,然后进行h264视频编码和aac音频编码,并发送到RTMP服务器,从而实现Android摄像头直播.程序名为NdkRtmpEncoder,在这里把整个过程,和大体框架介绍一下,算是给需要的人引路. 开发思路 首先,为什么要用NDK来做,因为自己之前就已经实现过RTMP推流.RTMP播放.RTSP转码等等各种

Mybatis学习记录(二)--Mybatis开发DAO方式

mybatis开发dao的方法通常用两种,一种是传统DAO的方法,一种是基于mapper代理的方法,下面学习这两种开发模式. 写dao之前应该要对SqlSession有一个更加细致的了解 一.mybatis的SqlSession使用范围 SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产

Zephyr学习(二)开发环境搭建

一.概述 Zephyr支持在Windows.Linux和MacOS环境下开发,这里只介绍如何在Windows下搭建zephyr的开发环境. 二.步骤 2.1安装msys2 msys2是一个Linux模拟环境,类似于ArchLinux. 安装完成后,要添加源(这与Linux环境是类似的),这里我添加的是清华大学的源,修改C:\msys64\etc\pacman.d\mirrorlist.mingw32,在开始处加入以下一行: Server = https://mirrors.tuna.tsingh

1,下载和部署开发环境--AutoCAD二次开发(2020版)

教程说明: 本教程为2019年10月开始编撰,使用CAD官方最新版本的软件和库.对旧版本仍有参考价值. 本教程中使用的各种软件版本为官方指定匹配版本. 本教程需要你拥有编程基础,对于普通编程常识不再敷述.当然,你可以留言提问. 环境需求为: AutoCAD 2020版 ObjectARX SDK 下载地址:https://www.autodesk.com/developer-network/platform-technologies/autocad/objectarx-license-downl

微信开发学习日记(二):3个案例

上次是调通了"消息来自微信请求"一个请求验证接口.    今天下午,正式进军微信开发,完成了3个案例的demo,测试通过. 上次,提到读了5本书,4本PHP描述的,一本Java描述的.个人专注Java开发7年了,更倾向于用Java,当然PHP也要立即着手深入学习了.今天好几个PHP的外包项目,感觉太麻烦,都拒绝了. Java的书,柳峰写的那本就非常好,至少可以用Good描述,Perfect的话,要看是否还有更多更优秀Java语言相关的微信书籍. 那本书的源码,网上都有,下载下来之后,全

微信小程序开发学习(二)

一些官方API 总结了一些官方API,便于之后有用时针对性查找(发现官方给了好多好用的API)官方API文档 基础 wx.canIUse:判断小程序的API,回调,参数,组件等是否在当前版本可用,返回值为boolean. 使用${API}.${method}.${param}.${options}或者${component}.${attribute}.${option}方式来调用 参数说明 ${API}代表API名字 ${method}代表调用方式,有效值为return,success, obj

微信开发学习(二)

学习发送微信的消息类型. 当发送消息时,同时会附加的参数有,signature,timestamp,nonce, 以及消息的XML数据包. 数据包格式,类似: <xml> <ToUserName><![CDATA[tousername]]></ToUserName> <FromUserName><![CDATA[fromusername]]></FromUserName> <CreateTime><!CD

Android开发学习总结(二)——使用Android Studio搭建Android集成开发环境

有很长一段时间没有更新博客了,最近实在是太忙了,没有时间去总结,现在终于可以有时间去总结一些Android上面的东西了,很久以前写过这篇关于使用Android Studio搭建Android集成开发环境,不过一直没有发表出来,写这篇文章的目的是记录一下Android开发环境的搭建过程,这篇文章写得一般般,主要是记录了整个搭建过程,没什么技术含量,觉得有帮助的朋友就看一下! 一.Android Studio简单介绍 2013年GoogleI/O大会首次发布了Android Studio IDE(A

web多终端开发学习系列(二)--- 硬件交互框架Phonegap学习

目前对于移动端.桌面端的原生开发各平台都有各自的开发语言.开发工具等.如果要开发出一个适配多个终端的系统,我们需要用各自的平台工具分别开发出各自的应用,这对于一个团队或者一个开发人员来说要求非常高,并且非常耗时.前一篇学习了Bootstrap框架,对于多终端的界面适配有了解决方案.但一个系统除了与用户之间的交互外,其内部还可能需要与硬件进行交互来实现某些特殊需求,比如摄像头的调用,相册的获取等等.对于这些硬件的调用我们可以选择Phonegap. 介绍 Phonegap是一个适配多个终端进行硬件交