在上一篇文章寫了如何配置服務器:html
https://blog.csdn.net/qq_36313726/article/details/81027366java
今天我就給你們說下如何獲取用戶發送消息並實現回覆,本身在弄這個過程走了許多坑。服務器
要實現消息獲取和自動回覆,須要瞭解微信是怎麼實現這個過程:微信
我從微信官方文檔摘取了下面比較重要的信息微信開發
當普通微信用戶向公衆帳號發消息時,微信服務器將POST消息的XML數據包到開發者填寫的URL上。app
文本消息在請求的輸出流中,而不是參數位置,參數是對xml中標籤的解釋。dom
文本消息post
<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請求,開發者能夠在響應包(Get)中返回特定XML結構,來對該消息進行響應(現支持回覆文本、圖片、圖文、語音、視頻、音樂)。嚴格來講,發送被動響應消息其實並非一種接口,而是對微信服務器發過來消息的一次回覆。測試
回覆文本消息ui
<xml> <ToUserName>< ![CDATA[toUser] ]></ToUserName> <FromUserName>< ![CDATA[fromUser] ]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType>< ![CDATA[text] ]></MsgType> <Content>< ![CDATA[你好] ]></Content> </xml>
參數 | 是否必須 | 描述 |
---|---|---|
ToUserName | 是 | 接收方賬號(收到的OpenID) |
FromUserName | 是 | 開發者微信號 |
CreateTime | 是 | 消息建立時間 (整型) |
MsgType | 是 | text |
Content | 是 | 回覆的消息內容(換行:在content中可以換行,微信客戶端就支持換行顯示) |
在網上看到這張圖片很好理解
微信公衆號開發文檔說了,只有對服務器進行驗證的時候纔會使用到get請求到服務器的url中,其它一概使用post請求。
所以用戶發送消息時,微信服務器是經過post請求到咱們的服務器上。
能夠看下我寫的代碼來理解這個過程,這個代碼有部分利用上一篇文章說過的步驟:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.qq.weixin.mp.aes.AesException; import com.qq.weixin.mp.aes.SHA1; /** * Servlet implementation class test */ @WebServlet("/test") public class test extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); String signature = request.getParameter("signature"); String timestamp = request.getParameter("timestamp"); String nonce = request.getParameter("nonce"); String echostr = request.getParameter("echostr"); String token = "jylife"; String jiami = ""; String openid=request.getParameter("openid"); String body=request.getParameter("body"); System.out.println(body); Date date=new Date(); try { jiami=SHA1.getSHA1(token, timestamp, nonce,"");//這裏是對三個參數進行加密 } catch (AesException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("加密"+jiami); System.out.println("自己"+signature); PrintWriter out=response.getWriter(); if(jiami.equals(signature)) out.print(echostr); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); //獲取服務器發送過來的信息,由於不是參數,得用輸入流讀取 BufferedReader reader = null; StringBuilder sb = new StringBuilder(); try{ reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "utf-8")); String line = null; while ((line = reader.readLine()) != null){ sb.append(line); } } catch (IOException e){ e.printStackTrace(); } finally { try{ if (null != reader){ reader.close();} } catch (IOException e){ e.printStackTrace(); } } System.out.println("用戶發送過來信息:"+sb);//將用戶發送得消息打印出來 /* 能夠request看出全部的參數信息 * Enumeration enu=request.getParameterNames(); while(enu.hasMoreElements()){ String paraName=(String)enu.nextElement(); System.out.println(paraName+": "+request.getParameter(paraName)); } */ String openid=request.getParameter("openid");//獲取參數中的openid PrintWriter out=response.getWriter(); String replyMsg = "<xml>" + "<ToUserName><![CDATA["+openid+"]]></ToUserName>"//回覆用戶時,這裏是用戶的openid;但用戶發送過來消息這裏是微信公衆號的原始id + "<FromUserName><![CDATA["+"這裏微信公衆號的原始id"+"]]></FromUserName>"//這裏填寫微信公衆號 的原始id;用戶發送過來時這裏是用戶的openid + "<CreateTime>1531553112194</CreateTime>"//這裏能夠填建立信息的時間,目前測試隨便填也能夠 + "<MsgType><![CDATA[text]]></MsgType>"//文本類型,text,能夠不改 + "<Content><![CDATA[我喜歡你]]></Content>"//文本內容,我喜歡你 + "<MsgId>1234567890123456</MsgId> "//消息id,隨便填,但位數要夠 + " </xml>"; System.out.println(replyMsg);//打印出來 out.println(replyMsg);//回覆 } }
這裏仍是強調一下,文本消息在輸出流中,獲取參數是看不到的;若是仍是有點犯暈,能夠將裏面註釋掉的輸出所有request參數代碼部分運行一下。
最後我參考微信官方給的實例,對代碼作了一點適配,能夠不須要手動配置微信開發者的初始id
import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.qq.weixin.mp.aes.AesException; import com.qq.weixin.mp.aes.SHA1; /** * Servlet implementation class test */ @WebServlet("/test") public class test extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); String signature = request.getParameter("signature"); String timestamp = request.getParameter("timestamp"); String nonce = request.getParameter("nonce"); String echostr = request.getParameter("echostr"); String token = "jylife"; String jiami = ""; String openid=request.getParameter("openid"); String body=request.getParameter("body"); System.out.println(body); Date date=new Date(); try { jiami=SHA1.getSHA1(token, timestamp, nonce,"");//這裏是對三個參數進行加密 } catch (AesException e) { // TODO Auto-generated catch block e.printStackTrace(); } PrintWriter out=response.getWriter(); if(jiami.equals(signature)) out.print(echostr); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); PrintWriter out=response.getWriter(); try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document document = db.parse(request.getInputStream()); Element root = document.getDocumentElement(); String wechatId = root.getElementsByTagName("ToUserName").item(0).getTextContent(); String openid = root.getElementsByTagName("FromUserName").item(0).getTextContent(); String msg = root.getElementsByTagName("Content").item(0).getTextContent();//用戶發送的內容 System.out.println(msg);//打印用戶發送的消息 String content=""; //對用戶發送過來的內容選擇要回復的內容 if(msg.matches("喜歡")) { content="我也喜歡你"; } else { content="我也不喜歡你"; } String replyMsg = "<xml>" + "<ToUserName><![CDATA["+openid+"]]></ToUserName>"//回覆用戶時,這裏是用戶的openid;但用戶發送過來消息這裏是微信公衆號的原始id + "<FromUserName><![CDATA["+wechatId+"]]></FromUserName>"//這裏填寫微信公衆號 的原始id;用戶發送過來時這裏是用戶的openid + "<CreateTime>1531553112194</CreateTime>"//這裏能夠填建立信息的時間,目前測試隨便填也能夠 + "<MsgType><![CDATA[text]]></MsgType>"//文本類型,text,能夠不改 + "<Content><![CDATA["+content+"]]></Content>"//文本內容,我喜歡你 + "<MsgId>1234567890123456</MsgId> "//消息id,隨便填,但位數要夠 + " </xml>"; System.out.println(replyMsg);//打印出來 out.println(replyMsg);//回覆 } catch(Exception e) { System.out.println(e.toString()); } } }