1. 程式人生 > >微信開發學習總結(二)——微信開發入門

微信開發學習總結(二)——微信開發入門

一、微信公眾平臺的基本原理

  在開始做之前,先簡單介紹了微信公眾平臺的基本原理。

  微信伺服器就相當於一個轉發伺服器,終端(手機、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>

複製程式碼

  測試公眾號的管理後臺也可以看到關注測試號的使用者列表,如下圖所示:

  

  通過這個簡單的入門程式,我們揭開了微信開