微信公众号开发:获取用户发送消息并实现回复(Java)
在上一篇文章写了如何配置服务器:
https://blog.csdn.net/qq_36313726/article/details/81027366
今天我就给大家说下如何获取用户发送消息并实现回复,自己在弄这个过程走了许多坑。
要实现消息获取和自动回复,需要了解微信是怎么实现这个过程:
我从微信官方文档摘取了下面比较重要的信息
接收普通消息
当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
文本消息在请求的输出流中,而不是参数位置,参数是对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请求,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。严格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。
回复文本消息
<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()); } } }
多一份坚持,少一份懒惰