1. 程式人生 > >微信開發學習總結(三)——訊息管理(1)

微信開發學習總結(三)——訊息管理(1)

上一節內容:
微信開發學習總結(二)——微信開發環境準備(2)
https://blog.csdn.net/qq_29914837/article/details/82896861


接收普通訊息

當普通微信使用者向公眾賬號發訊息時,微信伺服器將POST訊息的XML資料包到開發者填寫的URL上。

請注意:
1、關於重試的訊息排重,推薦使用msgid排重。
2、微信伺服器在五秒內收不到響應會斷掉連線,並且重新發起請求,總共重試三次。假如伺服器無法保證在五秒內處理並回復,可以直接回復空串,微信伺服器不會對此作任何處理,並且不會發起重試。詳情請見“傳送訊息-被動回覆訊息”。
3、如果開發者需要對使用者訊息在5秒內立即做出迴應,即使用“傳送訊息-被動回覆訊息”介面向用戶被動回覆訊息時,可以在公眾平臺官網的開發者中心處設定訊息加密。開啟加密後,使用者發來的訊息和開發者回覆的訊息都會被加密(但開發者通過客服介面等API呼叫形式向用戶傳送訊息,則不受影響)。關於訊息加解密的詳細說明,請見“傳送訊息-被動回覆訊息加解密說明”。

一、文字訊息推送XML資料包結構

<xml>  <ToUserName>< ![CDATA[toUser] ]></ToUserName>  <FromUserName>< ![CDATA[fromUser] ]></FromUserName>  <CreateTime>1348831860</CreateTime>  <MsgType>< ![CDATA[text] ]></MsgType>  <Content>< ![CDATA[this is a test] ]></Content>  <MsgId>1234567890123456</MsgId>  </xml>
引數 描述
ToUserName 開發者微訊號
FromUserName 傳送方帳號(一個OpenID)
CreateTime 訊息建立時間 (整型)
MsgType text
Content 文字訊息內容
MsgId 訊息id,64位整型

二、接收微信伺服器傳送的訊息並做出響應

通過下面程式碼來實現接收微信伺服器傳送的訊息並做出響應的功能。

當普通微信使用者向公眾賬號發訊息時,微信伺服器將POST訊息的XML資料包到開發者填寫的URL上(也就是我們在伺服器配置的URL)。

①WeiXinCheck.java(工具類,包含各種常用方法)

package weixin.util;

import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import net.sf.json.JSONObject;
import weixin.entity.accesstoken.AccessToken;

/**
 * @所屬類別:工具類
 * @用途:微信開發校驗方法、sha1加密演算法等
 * @author yilei
 * @version:1.0
 */
public class WeiXinCheck {

	/**
	 * @method 將token、timestamp、nonce三個引數進行字典序排序
	 * @param token
	 * @param timestamp
	 * @param nonce
	 * @return 排序後的字串
	 */
	public static String sort(String token, String timestamp, String nonce) {
		String[] strArray = { token, timestamp, nonce };
		Arrays.sort(strArray);
		StringBuilder sb = new StringBuilder();
		for (String str : strArray) {
			sb.append(str);
		}
		return sb.toString();
	}

	/**
	 * @method 將三個引數字串拼接成一個字串進行sha1加密
	 * @param str,需要加密的字串(排序後的字串)
	 * @return 加密後的內容
	 */
	public static String sha1(String str) {
		try {
			MessageDigest digest = MessageDigest.getInstance("SHA-1");
			digest.update(str.getBytes());
			byte messageDigest[] = digest.digest();
			// Create Hex String
			StringBuffer hexString = new StringBuffer();
			// 位元組陣列轉換為 十六進位制 數
			for (int i = 0; i < messageDigest.length; i++) {
				String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
				if (shaHex.length() < 2) {
					hexString.append(0);
				}
				hexString.append(shaHex);
			}
			return hexString.toString();

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return "";
	}

	/**
	 * @method 開發者獲得加密後的字串可與signature對比
	 * @param str ,需要加密的字串(排序後的字串)
	 * @return 加密後的內容
	 */
	public static boolean equalSignature(String str, String signature) {
		boolean falg = false;
		if (str != null && str != "") {
			if (str.equals(signature)) {
				falg = true;
			}
		}
		return falg;

	}

	/**
	 * 發起Http請求, 通過GET方式訪問網路用到的方法
	 * @param url,請求的URL地址
	 * @return 響應後的字串
	 */
	public static JSONObject doGetStr(String url){
		DefaultHttpClient httpClient = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet(url);
		JSONObject jsonObject = null;
		try {
			HttpResponse response =  httpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			if(entity!=null){
				String result=EntityUtils.toString(entity);
				jsonObject = JSONObject.fromObject(result);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return jsonObject;
	}
	
	/**
	 * 發起Http請求, 通過POST方式訪問網路用到的方法
	 * @param url,請求的URL地址
	 * @return 響應後的字串
	 */
	public static JSONObject doPostStr(String url,String outstr){
		DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(url);
        JSONObject jsonObject = null;
		try {
			httpPost.setEntity(new StringEntity(outstr, "UTF-8"));
			HttpResponse response = httpClient.execute(httpPost);
			HttpEntity entity = response.getEntity();
			String result=EntityUtils.toString(entity,"UTF-8");
			jsonObject = JSONObject.fromObject(result);
		
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		return jsonObject;
		
	}

	/**
	 * 獲取access_token
	 * @return AccessToken物件
	 */
	public static AccessToken getAccessToken() {
		AccessToken accessToken = new AccessToken();
		String url = WeiXin.ACCESS_TOKEN_URL.replace("APPID", WeiXin.APPID).replace("APPSECRET", WeiXin.APPSECRET);
		JSONObject jsonObject = WeiXinCheck.doGetStr(url);
		if (jsonObject != null) {
			accessToken.setAccessToken(jsonObject.getString("access_token"));
			accessToken.setExpiresin(jsonObject.getInt("expires_in"));
		}
		return accessToken;
	}
	
    /**
     * 解析微信發來的請求(XML)並且轉換為Map
     * @param request
     * @return map
     * @throws Exception
     */
	public static Map<String,String> parseXml(HttpServletRequest request) throws Exception {
        // 將解析結果儲存在HashMap中
        Map<String,String> map = new HashMap();
        // 從request中取得輸入流
        InputStream inputStream = request.getInputStream();
        // 讀取輸入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子節點
        List<Element> elementList = root.elements();

        // 遍歷所有子節點
        for (Element e : elementList) {
            System.out.println(e.getName() + "|" + e.getText());
            map.put(e.getName(), e.getText());
        }

        // 釋放資源
        inputStream.close();
        return map;
    }
	
    // 根據訊息型別 構造返回訊息
    public static String buildXml(Map<String,String> map) {
        String result;
        String msgType = map.get("MsgType").toString();
        if(msgType.toUpperCase().equals("TEXT")){//文字訊息
            result = buildTextMessage(map, "歡迎豬牧狼馬蜂YY,訊息型別:文字訊息");
        }else{
            String fromUserName = map.get("FromUserName");
            // 開發者微訊號
            String toUserName = map.get("ToUserName");
            result = String
                    .format(
                            "<xml>" +
                                    "<ToUserName><![CDATA[%s]]></ToUserName>" +
                                    "<FromUserName><![CDATA[%s]]></FromUserName>" +
                                    "<CreateTime>%s</CreateTime>" +
                                    "<MsgType><![CDATA[text]]></MsgType>" +
                                    "<Content><![CDATA[%s]]></Content>" +
                            "</xml>",
                            fromUserName, toUserName, getCreateTime(),
                            "請回復如下關鍵詞:\n文字\n圖片\n語音\n視訊\n音樂\n圖文");
        }

        return result;
    }

    /**
     * 構造文字訊息
     * @param map
     * @param content
     * @return
     */
    private static String buildTextMessage(Map<String,String> map, String content) {
        //傳送方帳號
        String fromUserName = map.get("FromUserName");
        // 開發者微訊號
        String toUserName = map.get("ToUserName");
        return String.format(
                "<xml>" +
                        "<ToUserName><![CDATA[%s]]></ToUserName>" +
                        "<FromUserName><![CDATA[%s]]></FromUserName>" +
                        "<CreateTime>%s</CreateTime>" +
                        "<MsgType><![CDATA[text]]></MsgType>" +
                        "<Content><![CDATA[%s]]></Content>" + "</xml>",
                fromUserName, toUserName, getCreateTime(), content);
    }
    
    /**
     * 格式化日期格式
     * @return 格式化的日期
     */
    public static String getCreateTime() {
        Date dt = new Date();// 如果不需要格式,可直接用dt,dt就是當前系統時間
        DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");// 設定顯示格式
        String nowTime = df.format(dt);
        long dd = (long) 0;
        try {
            dd = df.parse(nowTime).getTime();
        } catch (Exception e) {

        }
        return String.valueOf(dd);
    }
	
}

②MainApplication.java (微信開發介面入口)

package weixin;

import java.io.IOException;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import weixin.util.WeiXin;
import weixin.util.WeiXinCheck;
/**
 * @所屬類別:servlet類
 * @用途:微信開發介面入口
 * @author yilei
 * @version:1.0
 */
public class MainApplication extends HttpServlet{

	/**
	 * 開發者提交資訊後,微信伺服器將傳送GET請求到填寫的伺服器地址URL上(校驗簽名是否通過,通過才可以進行微信開發其他操作)
	 * @param request
	 * @param response
	 * @throws IOException 
	 */
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		 // TODO Auto-generated method stub
		 System.out.println("開始校驗簽名");
		 //接收微信伺服器傳送請求時傳遞過來的4個引數
		 String signature = request.getParameter("signature");//微信加密簽名signature結合了開發者填寫的token引數和請求中的timestamp引數、nonce引數。
		 String timestamp = request.getParameter("timestamp");//時間戳
		 String nonce = request.getParameter("nonce");//隨機數
		 String echostr = request.getParameter("echostr");//隨機字串
		 //排序
		 String sortString = WeiXinCheck.sort(WeiXin.TOKEN, timestamp, nonce);
		 //sha1加密
		 String mySignature = WeiXinCheck.sha1(sortString);
		 //校驗簽名
		 if (WeiXinCheck.equalSignature(mySignature, signature)) {
	            System.out.println("簽名校驗通過。");
	            //如果檢驗成功輸出echostr,微信伺服器接收到此輸出,才會確認檢驗完成。
	            //response.getWriter().println(echostr);
	            response.getWriter().println(echostr);
	        } else {
	            System.out.println("簽名校驗失敗.");
	        }
	}
	
	/**
	 * 處理微信伺服器發來的訊息
	 */
    @Override
	public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
        // TODO 接收、處理、響應由微信伺服器轉發的使用者傳送給公眾帳號的訊息
        // 將請求、響應的編碼均設定為UTF-8(防止中文亂碼)
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        System.out.println("微信的post請求進入了本地伺服器了");
        String result = "";
        try {
            Map<String,String> map = WeiXinCheck.parseXml(request);
            System.out.println("微信公眾號要開始傳送訊息");
            result = WeiXinCheck.buildXml(map);
            System.out.println(result);
            if(result.equals("")){
                result = "未正確響應";
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("發生異常:"+ e.getMessage());
        }
        response.getWriter().println(result);
    }
	
}

啟動伺服器,具體步驟參考前面幾節課程

然後,關注微信測試公眾號
在這裡插入圖片描述

在這裡插入圖片描述

微信開發學習總結(三)——訊息管理(1)——專案原始碼
下載地址:
https://download.csdn.net/download/qq_29914837/10696854


下一節內容:
微信開發學習總結(三)——訊息管理(2)
https://blog.csdn.net/qq_29914837/article/details/82904454