在上一篇文章写了如何配置服务器:
https://blog.youkuaiyun.com/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());
}
}
}