上一节内容:
微信开发学习总结(二)——微信开发环境准备(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