GitHub源码:https://github.com/shirayner/weixin_gz

一、本节要点

1.回调url

上一节,我们启用服务器配置的时候,填写了一个服务器地址(url),如下图,这个url就是回调url,是开发者用来接收微信消息和事件的接口URL 。也就是说,用户在微信公众号中发送的消息会被推送到这个回调url,而我们可以接收用户的消息,并进行回复。

2.被动回复消息的流程

官方文档:

我们在上一节中设置的消息加解密方式是安全模式。因此在用户发给公众号的消息(接收消息)以及公众号被动回复用户消息(回复消息)都会加密,

流程

用户发送消息之后,微信服务器将消息传递给 第三方服务器,第三方服务器接收到消息后,再对消息做出相应的回复消息。

接收消息:需先从request请求对象的输入流中获取请求参数和已加密的请求消息,再对已加密的请求消息进行解密操作,即可获得明文。

然后就行对明文消息的业务处理了。

回复消息:封装好回复消息后,需先对回复消息进行加密,获得已已加密消息,然后再通过http请求调用被动回复消息的接口,来发送消息。

3.被动回复消息加解密

3.1接收消息的  解密

(1)从请求的输入流中获取加密的请求消息

            //1.获取加密的请求消息:使用输入流获得加密请求消息postDataServletInputStream in = request.getInputStream();BufferedReader reader =new BufferedReader(new InputStreamReader(in));  String tempStr="";   //作为输出字符串的临时串,用于判断是否读取完毕  while(null!=(tempStr=reader.readLine())){  postData+=tempStr;  }  logger.info("postData:"+postData);

View Code

(2)对加密的请求消息进行解密获得明文

                WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);result=wxcpt.DecryptMsg(msgSignature, timeStamp, nonce, postData);

View Code

(3)解密算法

直接调用微信官方的 WXBizMsgCrypt 类的 DecryptMsg(String, String, String, String) 方法即可。

3.2 回复消息的加密

直接用官方加解密工具类。

                wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);replyMsg=wxcpt.EncryptMsg(replyMsg, timeStamp, nonce);

View Code

4.消息对象的封装

根据官方文档消息的xml传输格式,我们可以将消息封装成对象。请参见后面的代码实现部分

5.数据传输—对象 转成  xml字符串

根据官方文档,数据是以XML数据包的形式进行传输的。因此,我们需要

(1)解析微信发来的请求(xmlStr),从xml字符串中获取需要的信息

(2)回复消息时,将消息对象转成xml字符串。

我们是使用dom4j,xstream来进行这个转换的,因此需要导入jar包,maven依赖如下:

<!-- 7.XML解析 --><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><dependency><groupId>com.thoughtworks.xstream</groupId><artifactId>xstream</artifactId><version>1.4.10</version></dependency>

具体请参见代码实现的 MessageUtil 部分。

5.1  解析微信发来的请求(XML),获取请求参数

  /*** @desc :1.解析微信发来的请求(XML),获取请求参数 *  * @param request* @return* @throws Exception Map<String,String>*/public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {  // 将解析结果存储在HashMap中  Map<String, String> map = new HashMap<String, String>();  // 从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)  map.put(e.getName(), e.getText());  // 释放资源
        inputStream.close();  inputStream = null;  return map;  } 

View Code

5.2 将文本消息转成xml字符串

  /** * 2.文本消息对象转换成xml *  * @param textMessage 文本消息对象 * @return xml */  public static String textMessageToXml(TextMessage textMessage) {  xstream.alias("xml", textMessage.getClass());  return xstream.toXML(textMessage);  }  

View Code

二、代码实现

1.微信配置类—Env.java

微信公众号接入配置类

package com.ray.weixin.gz.config;/**@desc  : 微信公众号接入配置* * @author: shirayner* @date  : 2017年9月27日 下午4:57:36*/public class Env {/*** 1. 企业应用接入秘钥相关*/// public static final String APP_ID = "wx4ddse2334debebef2cc";//public static final String APP_SECRET = "068e2599abf88ba72frrgbfs6f3c56e";//测试号public static final String APP_ID = "wxa00642deff56g062";public static final String APP_SECRET = "fcc96fefdgdhtj1a46af7993784917";/*** 2.服务器配置:* 启用并设置服务器配置后,用户发给公众号的消息以及开发者需要的事件推送,将被微信转发到该URL中*/public static final String TOKEN = "weixin";public static final String ENCODING_AES_KEY = "JvJ1Dww6tjUU2psC3pdewegreHfovfWP3LfX1xrriz1";}

View Code

2.HTTP请求工具类—HttpHelper.java

package com.ray.weixin.gz.util;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.util.EntityUtils;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;/*** HTTP请求封装,建议直接使用sdk的API*/
public class HttpHelper {/*** @desc :1.发起GET请求*  * @param url* @return JSONObject* @throws Exception */public static JSONObject doGet(String url) throws Exception {//1.生成一个请求HttpGet httpGet = new HttpGet(url);//2.配置请求的属性RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();//2000
        httpGet.setConfig(requestConfig);//3.发起请求,获取响应信息    //3.1 创建httpClient CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = null;try {//3.2 发起请求,获取响应信息    response = httpClient.execute(httpGet, new BasicHttpContext());//如果返回结果的code不等于200,说明出错了  if (response.getStatusLine().getStatusCode() != 200) {System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()+ ", url=" + url);return null;}//4.解析请求结果HttpEntity entity = response.getEntity();      //reponse返回的数据在entity中 if (entity != null) {String resultStr = EntityUtils.toString(entity, "utf-8");  //将数据转化为string格式  System.out.println("GET请求结果:"+resultStr);JSONObject result = JSON.parseObject(resultStr);    //将String转换为 JSONObjectif(result.getInteger("errcode")==null) {return result;}else if (0 == result.getInteger("errcode")) {return result;}else {System.out.println("request url=" + url + ",return value=");System.out.println(resultStr);int errCode = result.getInteger("errcode");String errMsg = result.getString("errmsg");throw new Exception("error code:"+errCode+", error message:"+errMsg); }}} catch (IOException e) {System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());e.printStackTrace();} finally {if (response != null) try {response.close();                     //释放资源
} catch (IOException e) {e.printStackTrace();}}return null;}/** 2.发起POST请求* @desc :*  * @param url   请求url* @param data  请求参数(json)* @return* @throws Exception JSONObject*/public static JSONObject doPost(String url, Object data) throws Exception {//1.生成一个请求HttpPost httpPost = new HttpPost(url);//2.配置请求属性//2.1 设置请求超时时间RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();httpPost.setConfig(requestConfig);//2.2 设置数据传输格式-jsonhttpPost.addHeader("Content-Type", "application/json");//2.3 设置请求实体,封装了请求参数StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8");httpPost.setEntity(requestEntity);//3.发起请求,获取响应信息    //3.1 创建httpClient CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = null;try {//3.3 发起请求,获取响应response = httpClient.execute(httpPost, new BasicHttpContext());if (response.getStatusLine().getStatusCode() != 200) {System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()+ ", url=" + url);return null;}//获取响应内容HttpEntity entity = response.getEntity();if (entity != null) {String resultStr = EntityUtils.toString(entity, "utf-8");System.out.println("POST请求结果:"+resultStr);//解析响应内容JSONObject result = JSON.parseObject(resultStr);if(result.getInteger("errcode")==null) {return result;}else if (0 == result.getInteger("errcode")) {return result;}else {System.out.println("request url=" + url + ",return value=");System.out.println(resultStr);int errCode = result.getInteger("errcode");String errMsg = result.getString("errmsg");throw new Exception("error code:"+errCode+", error message:"+errMsg); }}} catch (IOException e) {System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());e.printStackTrace();} finally {if (response != null) try {response.close();              //释放资源
} catch (IOException e) {e.printStackTrace();}}return null;}/*** @desc : 3.上传文件*  * @param url   请求url* @param file  上传的文件* @return* @throws Exception JSONObject*/public static JSONObject uploadMedia(String url, File file) throws Exception {HttpPost httpPost = new HttpPost(url);CloseableHttpResponse response = null;CloseableHttpClient httpClient = HttpClients.createDefault();RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();httpPost.setConfig(requestConfig);//2.3 设置请求实体,封装了请求参数HttpEntity requestEntity = MultipartEntityBuilder.create().addPart("media",new FileBody(file, ContentType.create("multipart/form-data", Consts.UTF_8), file.getName())).build();//FileEntity requestEntity = new FileEntity(file,ContentType.MULTIPART_FORM_DATA);
httpPost.setEntity(requestEntity);try {response = httpClient.execute(httpPost, new BasicHttpContext());if (response.getStatusLine().getStatusCode() != 200) {System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()+ ", url=" + url);return null;}HttpEntity entity = response.getEntity();if (entity != null) {String resultStr = EntityUtils.toString(entity, "utf-8");JSONObject result = JSON.parseObject(resultStr);//上传临时素材成功if (result.getString("errcode")== null) {// 成功//result.remove("errcode");//result.remove("errmsg");return result;} else {System.out.println("request url=" + url + ",return value=");System.out.println(resultStr);int errCode = result.getInteger("errcode");String errMsg = result.getString("errmsg");throw new Exception("error code:"+errCode+", error message:"+errMsg); }}} catch (IOException e) {System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());e.printStackTrace();} finally {if (response != null) try {response.close();                  //释放资源
} catch (IOException e) {e.printStackTrace();}}return null;}/*** @desc : 上传PDF* 见微信电子发票章节* 9. 向用户提供发票或其它消费凭证PDF*  * @param url* @param file* @return* @throws Exception *   JSONObject*/public static JSONObject uploadPDF(String url, File file) throws Exception {HttpPost httpPost = new HttpPost(url);CloseableHttpResponse response = null;CloseableHttpClient httpClient = HttpClients.createDefault();RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();httpPost.setConfig(requestConfig);//2.3 设置请求实体,封装了请求参数HttpEntity requestEntity = MultipartEntityBuilder.create().addPart("media",new FileBody(file, ContentType.create("multipart/form-data", Consts.UTF_8), file.getName())).build();httpPost.setEntity(requestEntity);try {response = httpClient.execute(httpPost, new BasicHttpContext());if (response.getStatusLine().getStatusCode() != 200) {System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()+ ", url=" + url);return null;}HttpEntity entity = response.getEntity();if (entity != null) {String resultStr = EntityUtils.toString(entity, "utf-8");JSONObject result = JSON.parseObject(resultStr);//上传临时素材成功if (result.getString("errcode")== null) {// 成功//result.remove("errcode");//result.remove("errmsg");return result;} else {System.out.println("request url=" + url + ",return value=");System.out.println(resultStr);int errCode = result.getInteger("errcode");String errMsg = result.getString("errmsg");throw new Exception("error code:"+errCode+", error message:"+errMsg); }}} catch (IOException e) {System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());e.printStackTrace();} finally {if (response != null) try {response.close();                  //释放资源
} catch (IOException e) {e.printStackTrace();}}return null;}/*** @desc : 4.下载文件 -get*  * @param url  请求url* @param fileDir  下载路径* @return* @throws Exception File*/public static File downloadMedia(String url, String fileDir) throws Exception  {//1.生成一个请求HttpGet httpGet = new HttpGet(url);//2.配置请求属性RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();httpGet.setConfig(requestConfig);//3.发起请求,获取响应信息    //3.1 创建httpClient CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = null;//4.设置本地保存的文件  //File file = new File(fileDir);File file = null;try {//5. 发起请求,获取响应信息    response = httpClient.execute(httpGet, new BasicHttpContext());System.out.println("HttpStatus.SC_OK:"+HttpStatus.SC_OK);  System.out.println("response.getStatusLine().getStatusCode():"+response.getStatusLine().getStatusCode());  System.out.println("http-header:"+JSON.toJSONString( response.getAllHeaders() ));  System.out.println("http-filename:"+getFileName(response) );  //请求成功  if(HttpStatus.SC_OK==response.getStatusLine().getStatusCode()){  //6.取得请求内容  HttpEntity entity = response.getEntity();  if (entity != null) {  //这里可以得到文件的类型 如image/jpg /zip /tiff 等等 但是发现并不是十分有效,有时明明后缀是.rar但是取到的是null,这点特别说明
                    System.out.println(entity.getContentType());  //可以判断是否是文件数据流
                    System.out.println(entity.isStreaming());  //6.1 输出流//6.1.1获取文件名,拼接文件路径String fileName=getFileName(response);fileDir=fileDir+fileName;file = new File(fileDir);//6.1.2根据文件路径获取输出流FileOutputStream output = new FileOutputStream(file);  //6.2 输入流:从钉钉服务器返回的文件流,得到网络资源并写入文件  InputStream input = entity.getContent();  //6.3 将数据写入文件:将输入流中的数据写入到输出流byte b[] = new byte[1024];  int j = 0;  while( (j = input.read(b))!=-1){  output.write(b,0,j);  }  output.flush();  output.close();   }  if (entity != null) {  entity.consumeContent();  }  }  } catch (IOException e) {System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());e.printStackTrace();} finally {if (response != null) try {response.close();                       //释放资源
} catch (IOException e) {e.printStackTrace();}}return file;}/*** @desc : 5.下载文件 - post*  * @param url  请求url* @param data  post请求参数* @param fileDir 文件下载路径* @return* @throws Exception File*/public static File downloadMedia(String url, Object data, String fileDir) throws Exception  {//1.生成一个请求HttpPost httpPost = new HttpPost(url);//2.配置请求属性//2.1 设置请求超时时间RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();httpPost.setConfig(requestConfig);//2.2 设置数据传输格式-jsonhttpPost.addHeader("Content-Type", "application/json");//2.3 设置请求参数StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8");httpPost.setEntity(requestEntity);//3.发起请求,获取响应信息    //3.1 创建httpClient CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = null;//4.设置本地保存的文件  //File file = new File(fileDir);File file = null;try {//5. 发起请求,获取响应信息    response = httpClient.execute(httpPost, new BasicHttpContext());System.out.println("HttpStatus.SC_OK:"+HttpStatus.SC_OK);  System.out.println("response.getStatusLine().getStatusCode():"+response.getStatusLine().getStatusCode());  System.out.println("http-header:"+JSON.toJSONString( response.getAllHeaders() ));  System.out.println("http-filename:"+getFileName(response) );  //请求成功  if(HttpStatus.SC_OK==response.getStatusLine().getStatusCode()){  //6.取得请求内容  HttpEntity entity = response.getEntity();  if (entity != null) {  //这里可以得到文件的类型 如image/jpg /zip /tiff 等等 但是发现并不是十分有效,有时明明后缀是.rar但是取到的是null,这点特别说明
                    System.out.println(entity.getContentType());  //可以判断是否是文件数据流
                    System.out.println(entity.isStreaming());  //6.1 输出流//6.1.1获取文件名,拼接文件路径String fileName=getFileName(response);fileDir=fileDir+fileName;file = new File(fileDir);//6.1.2根据文件路径获取输出流FileOutputStream output = new FileOutputStream(file);  //6.2 输入流:从钉钉服务器返回的文件流,得到网络资源并写入文件  InputStream input = entity.getContent();  //6.3 将数据写入文件:将输入流中的数据写入到输出流byte b[] = new byte[1024];  int j = 0;  while( (j = input.read(b))!=-1){  output.write(b,0,j);  }  output.flush();  output.close();   }  if (entity != null) {  entity.consumeContent();  }  }  } catch (IOException e) {System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());e.printStackTrace();} finally {if (response != null) try {response.close();                       //释放资源
} catch (IOException e) {e.printStackTrace();}}return file;}/** 5. 获取response header中Content-Disposition中的filename值 * @desc :*  * @param response  响应* @return String*/public static String getFileName(HttpResponse response) {  Header contentHeader = response.getFirstHeader("Content-Disposition");  String filename = null;  if (contentHeader != null) {  HeaderElement[] values = contentHeader.getElements();  if (values.length == 1) {  NameValuePair param = values[0].getParameterByName("filename");  if (param != null) {  try {  //filename = new String(param.getValue().toString().getBytes(), "utf-8");  //filename=URLDecoder.decode(param.getValue(),"utf-8");  filename = param.getValue();  } catch (Exception e) {  e.printStackTrace();  }  }  }  }  return filename;  }  }

View Code

3、接收消息的封装

3.1 消息基类—BaseMessage

package com.ray.weixin.gz.model.message.request;  /***   @desc  : 消息基类(普通用户 -> 公众帐号) * * @author: shirayner* @date  : 2017年11月13日 上午10:58:08*/
public class BaseMessage {  // 开发者微信号  private String ToUserName;  // 发送方帐号(一个OpenID)  private String FromUserName;  // 消息创建时间 (整型)  private long CreateTime;  // 消息类型(text/image/location/link)  private String MsgType;  // 消息id,64位整型  private long MsgId;  public String getToUserName() {  return ToUserName;  }  public void setToUserName(String toUserName) {  ToUserName = toUserName;  }  public String getFromUserName() {  return FromUserName;  }  public void setFromUserName(String fromUserName) {  FromUserName = fromUserName;  }  public long getCreateTime() {  return CreateTime;  }  public void setCreateTime(long createTime) {  CreateTime = createTime;  }  public String getMsgType() {  return MsgType;  }  public void setMsgType(String msgType) {  MsgType = msgType;  }  public long getMsgId() {  return MsgId;  }  public void setMsgId(long msgId) {  MsgId = msgId;  }
}  

View Code

3.2 文本消息—TextMessage

package com.ray.weixin.gz.model.message.request;  /*** @desc  : 文本消息 * * @author: shirayner* @date  : 2017年11月13日 上午11:04:09*/
public class TextMessage extends BaseMessage {  // 消息内容  private String Content;  public String getContent() {  return Content;  }  public void setContent(String content) {  Content = content;  }
}  

View Code

3.3 图片消息—ImageMessage

package com.ray.weixin.gz.model.message.request;  /*** @desc  : 图片消息 * * @author: shirayner* @date  : 2017年11月13日 上午11:04:33*/
public class ImageMessage extends BaseMessage {  // 图片链接  private String PicUrl;  public String getPicUrl() {  return PicUrl;  }  public void setPicUrl(String picUrl) {  PicUrl = picUrl;  }
}  

View Code

3.4 链接消息—LinkMessage

package com.ray.weixin.gz.model.message.request;  /*** @desc  :链接消息  * * @author: shirayner* @date  : 2017年11月13日 上午11:05:46*/
public class LinkMessage extends BaseMessage {  // 消息标题  private String Title;  // 消息描述  private String Description;  // 消息链接  private String Url;  public String getTitle() {  return Title;  }  public void setTitle(String title) {  Title = title;  }  public String getDescription() {  return Description;  }  public void setDescription(String description) {  Description = description;  }  public String getUrl() {  return Url;  }  public void setUrl(String url) {  Url = url;  }
}  

View Code

3.5 地理位置消息—LocationMessage

package com.ray.weixin.gz.model.message.request;  /*** @desc  : 地理位置消息 * * @author: shirayner* @date  : 2017年11月13日 上午11:07:39*/
public class LocationMessage extends BaseMessage {  // 地理位置维度  private String Location_X;  // 地理位置经度  private String Location_Y;  // 地图缩放大小  private String Scale;  // 地理位置信息  private String Label;  public String getLocation_X() {  return Location_X;  }  public void setLocation_X(String location_X) {  Location_X = location_X;  }  public String getLocation_Y() {  return Location_Y;  }  public void setLocation_Y(String location_Y) {  Location_Y = location_Y;  }  public String getScale() {  return Scale;  }  public void setScale(String scale) {  Scale = scale;  }  public String getLabel() {  return Label;  }  public void setLabel(String label) {  Label = label;  }
}  

View Code

3.6 音频消息—VoiceMessage

package com.ray.weixin.gz.model.message.request;  /*** @desc  :音频消息  * * @author: shirayner* @date  : 2017年11月13日 上午11:08:25*/
public class VoiceMessage extends BaseMessage {  // 媒体ID  private String MediaId;  // 语音格式  private String Format;  public String getMediaId() {  return MediaId;  }  public void setMediaId(String mediaId) {  MediaId = mediaId;  }  public String getFormat() {  return Format;  }  public void setFormat(String format) {  Format = format;  }
}  

View Code

4. 回复消息的封装

4.1 消息基类—BaseMessage

package com.ray.weixin.gz.model.message.response;  /*** @desc  : 消息基类(公众帐号 -> 普通用户) * * @author: shirayner* @date  : 2017年11月13日 上午11:10:32*/
public class BaseMessage {  // 接收方帐号(收到的OpenID)  private String ToUserName;  // 开发者微信号  private String FromUserName;  // 消息创建时间 (整型)  private long CreateTime;  // 消息类型(text/music/news)  private String MsgType;  // 位0x0001被标志时,星标刚收到的消息  private int FuncFlag;  public String getToUserName() {  return ToUserName;  }  public void setToUserName(String toUserName) {  ToUserName = toUserName;  }  public String getFromUserName() {  return FromUserName;  }  public void setFromUserName(String fromUserName) {  FromUserName = fromUserName;  }  public long getCreateTime() {  return CreateTime;  }  public void setCreateTime(long createTime) {  CreateTime = createTime;  }  public String getMsgType() {  return MsgType;  }  public void setMsgType(String msgType) {  MsgType = msgType;  }  public int getFuncFlag() {  return FuncFlag;  }  public void setFuncFlag(int funcFlag) {  FuncFlag = funcFlag;  }
}  

View Code

4.2 文本消息—TextMessage

package com.ray.weixin.gz.model.message.response;  /*** @desc  : * * @author: shirayner* @date  : 2017年11月13日 上午11:10:58*/
public class TextMessage extends BaseMessage {  // 回复的消息内容  private String Content;  public String getContent() {  return Content;  }  public void setContent(String content) {  Content = content;  }
}  

View Code

4.3 音乐消息—MusicMessage

Music

package com.ray.weixin.gz.model.message.response;  /*** @desc  : 音乐model * * @author: shirayner* @date  : 2017年11月13日 上午11:12:47*/
public class Music {  // 音乐名称  private String Title;  // 音乐描述  private String Description;  // 音乐链接  private String MusicUrl;  // 高质量音乐链接,WIFI环境优先使用该链接播放音乐  private String HQMusicUrl;  public String getTitle() {  return Title;  }  public void setTitle(String title) {  Title = title;  }  public String getDescription() {  return Description;  }  public void setDescription(String description) {  Description = description;  }  public String getMusicUrl() {  return MusicUrl;  }  public void setMusicUrl(String musicUrl) {  MusicUrl = musicUrl;  }  public String getHQMusicUrl() {  return HQMusicUrl;  }  public void setHQMusicUrl(String musicUrl) {  HQMusicUrl = musicUrl;  }  }  

View Code

MusicMessage

package com.ray.weixin.gz.model.message.response;  /*** @desc  : 音乐消息 * * @author: shirayner* @date  : 2017年11月13日 上午11:12:06*/
public class MusicMessage extends BaseMessage {  // 音乐  private Music Music;  public Music getMusic() {  return Music;  }  public void setMusic(Music music) {  Music = music;  }
}  

View Code

4.4 图文消息—NewsMessage

Article

package com.ray.weixin.gz.model.message.response;  /*** @desc  :  图文model * * @author: shirayner* @date  : 2017年11月13日 上午11:15:30*/
public class Article {  // 图文消息名称  private String Title;  // 图文消息描述  private String Description;  // 图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80,限制图片链接的域名需要与开发者填写的基本资料中的Url一致  private String PicUrl;  // 点击图文消息跳转链接  private String Url;  public String getTitle() {  return Title;  }  public void setTitle(String title) {  Title = title;  }  public String getDescription() {  return null == Description ? "" : Description;  }  public void setDescription(String description) {  Description = description;  }  public String getPicUrl() {  return null == PicUrl ? "" : PicUrl;  }  public void setPicUrl(String picUrl) {  PicUrl = picUrl;  }  public String getUrl() {  return null == Url ? "" : Url;  }  public void setUrl(String url) {  Url = url;  }  }  

View Code

NewsMessage

package com.ray.weixin.gz.model.message.response;  import java.util.List;  /*** @desc  : 图文消息* * @author: shirayner* @date  : 2017年11月13日 上午11:13:36*/
public class NewsMessage extends BaseMessage {  // 图文消息个数,限制为10条以内  private int ArticleCount;  // 多条图文消息信息,默认第一个item为大图  private List<Article> Articles;  public int getArticleCount() {  return ArticleCount;  }  public void setArticleCount(int articleCount) {  ArticleCount = articleCount;  }  public List<Article> getArticles() {  return Articles;  }  public void setArticles(List<Article> articles) {  Articles = articles;  }
}  

View Code

5.接收微信消息和事件—WeiXinServlet.java

package com.ray.weixin.gz.controller;
import java.io.IOException;
import java.io.PrintWriter;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.ray.weixin.gz.config.Env;
import com.ray.weixin.gz.service.message.ReplyMessageService;/*** Servlet implementation class WeiXinServlet*/
public class WeiXinServlet extends HttpServlet {private static final Logger logger = LogManager.getLogger(WeiXinServlet.class);private static final long serialVersionUID = 1L;/*** Default constructor. */public WeiXinServlet() {// TODO Auto-generated constructor stub
    }//1.接收  回调模式  的请求protected void doGet(HttpServletRequest request, HttpServletResponse response)  {logger.info("get--------------");//一、校验URL//1.准备校验参数// 微信加密签名  String msgSignature = request.getParameter("signature");  // 时间戳  String timeStamp = request.getParameter("timestamp");  // 随机数  String nonce = request.getParameter("nonce");  // 随机字符串  String echoStr = request.getParameter("echostr");  PrintWriter out=null;try {//2.校验url//2.1 创建加解密类WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);//2.2进行url校验//不抛异常就说明校验成功String sEchoStr= wxcpt.verifyUrl_WXGZ(msgSignature, Env.TOKEN, timeStamp, nonce,echoStr);//2.3若校验成功,则原样返回 echoStr
out = response.getWriter(); out.print(sEchoStr);  } catch (AesException e) {// TODO Auto-generated catch block
            e.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch block
            e.printStackTrace();} finally {if (out != null) {out.close();  out = null;                       //释放资源
            }}}//2.接收  微信消息和事件  的请求protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {logger.info("post--------------");//1.将请求、响应的编码均设置为UTF-8(防止中文乱码)  request.setCharacterEncoding("UTF-8");  response.setCharacterEncoding("UTF-8");  //2.调用消息业务类接收消息、处理消息  String respMessage = ReplyMessageService.reply(request);  //3.响应消息  PrintWriter out = response.getWriter();  out.print(respMessage);  out.close(); }}

View Code

6.被动回复消息业务类—ReplyMessageService.java

package com.ray.weixin.gz.service.message;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.Map;import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;import com.alibaba.fastjson.JSON;
import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.ray.weixin.gz.config.Env;
import com.ray.weixin.gz.model.message.response.TextMessage;
import com.ray.weixin.gz.util.MessageUtil;/**@desc  : 发送消息-被动回复消息业务类* Passive reply message* @author: shirayner* @date  : 2017年10月31日 下午12:24:41*/
public class ReplyMessageService {private static final Logger logger = LogManager.getLogger(ReplyMessageService.class);/*** @desc :1.回复消息*  * @param request* @return *   String 回复消息的加密xml字符串*/public static String reply( HttpServletRequest request ) {String parms=JSON.toJSONString(request.getParameterMap()); logger.info("parms:"+parms);//1.解密:从request中获取消息明文String xmlMsg=decryptMsg(request);logger.info(xmlMsg);//2.获取回复消息(明文)String replyMsg = getReplyMsg( xmlMsg);//3.根据消息加密方式判断是否加密String timeStamp = request.getParameter("timestamp");   // 时间戳    String nonce = request.getParameter("nonce");          // 随机数  String encryptType=request.getParameter("encrypt_type");//3.1 安全模式-加密:将回复消息加密if(null!=encryptType) {WXBizMsgCrypt wxcpt=null;try {wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);replyMsg=wxcpt.EncryptMsg(replyMsg, timeStamp, nonce);} catch (AesException e) {// TODO Auto-generated catch block
                e.printStackTrace();}}return replyMsg;}/*** @desc :2.从request中获取消息明文*  从request中获取加密消息,将其解密并返回* @param request* @return String   消息明文*/public static String decryptMsg(HttpServletRequest request) {String postData="";   // 密文,对应POST请求的数据String result="";     // 明文,解密之后的结果
String msgSignature = request.getParameter("msg_signature"); // 微信加密签名  String timeStamp = request.getParameter("timestamp");   // 时间戳    String nonce = request.getParameter("nonce");          // 随机数  String encryptType=request.getParameter("encrypt_type");try {//1.获取加密的请求消息:使用输入流获得加密请求消息postDataServletInputStream in = request.getInputStream();BufferedReader reader =new BufferedReader(new InputStreamReader(in));  String tempStr="";   //作为输出字符串的临时串,用于判断是否读取完毕  while(null!=(tempStr=reader.readLine())){  postData+=tempStr;  }  logger.info("postData:"+postData);//2.获取消息明文:对加密的请求消息进行解密获得明文 if(null!=encryptType) {logger.info("安全模式:消息被加密");WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);result=wxcpt.DecryptMsg(msgSignature, timeStamp, nonce, postData);}else {logger.info("明文模式");result=postData;}} catch (IOException e) {e.printStackTrace();} catch (AesException e) {e.printStackTrace();}  return result;}/*** @desc :获取回复消息*  * @param request* @return String  返回加密后的回复消息*/public static String getReplyMsg(String xmlMsg){String replyMsg = null; try {//2.解析微信发来的请求,解析xml字符串Map<String, String> requestMap= MessageUtil.parseXml(xmlMsg);    //3.获取请求参数//3.1 企业微信CorpID  String fromUserName = requestMap.get("FromUserName");  //3.2 成员UserIDString toUserName = requestMap.get("ToUserName");  //3.3 消息类型与事件 String msgType = requestMap.get("MsgType"); String eventType = requestMap.get("Event");  String eventKey = requestMap.get("EventKey"); logger.info("fromUserName:"+fromUserName);logger.info("toUserName:"+toUserName);logger.info("msgType:"+msgType);logger.info("Event:"+eventType+"  eventKey:"+eventKey);//4.组装 回复文本消息  TextMessage textMessage = new TextMessage();  textMessage.setToUserName(fromUserName);  textMessage.setFromUserName(toUserName);  textMessage.setCreateTime(new Date().getTime());  textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);  //4.1.获取回复消息的内容 :消息的分类处理String replyContent=getReplyContentByMsgType(msgType, eventType, eventKey);textMessage.setContent(replyContent);  System.out.println("replyContent:"+replyContent);//5.获取xml字符串: 将(被动回复消息型的)文本消息对象 转成  xml字符串replyMsg = MessageUtil.textMessageToXml(textMessage); } catch (Exception e) {e.printStackTrace();}  return replyMsg;}/*** @desc :3.处理消息:根据消息类型获取回复内容*  * @param msgType 消息类型* @return String 回复内容*/public static  String getReplyContentByMsgType(String msgType,String eventType,String eventKey){String replyContent="";//1.文本消息  if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {  replyContent = "您发送的是文本消息!";  }  //2.图片消息  else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {  replyContent = "您发送的是图片消息!";  }  //3.地理位置消息  else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) { replyContent = "您发送的是地理位置消息 !";  }  //4.链接消息  else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {  replyContent = "您发送的是链接消息!";  }  //5.音频消息  else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {  replyContent = "您发送的是音频消息!";  }//6.事件推送  else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) { replyContent=getReplyContentByEventType(eventType, eventKey);}//7.请求异常else {replyContent="请求处理异常,请稍候尝试!";}  return replyContent;}/*** @desc :5.处理消息:根据事件类型获取回复内容*  * @param eventType  事件类型* @param eventKey  事件key值* @return *   String*/public static String getReplyContentByEventType(String eventType,String eventKey){String respContent="";// 订阅  if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {  respContent = "欢迎关注!";  }  // 取消订阅  else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {  // TODO 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息
        } //上报地理位置事件else if(eventType.equals("LOCATION")){}// 自定义菜单点击事件  else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {  if (eventKey.equals("12")) {  } else if (eventKey.equals("13")) {  respContent = "周边搜索菜单项被点击!";  } else if (eventKey.equals("14")) {  respContent = "历史上的今天菜单项被点击!";  } else if (eventKey.equals("21")) {  respContent = "歌曲点播菜单项被点击!";  } else if (eventKey.equals("22")) {  respContent = "经典游戏菜单项被点击!";  } else if (eventKey.equals("23")) {  respContent = "美女电台菜单项被点击!";  } else if (eventKey.equals("24")) {  respContent = "人脸识别菜单项被点击!";  } else if (eventKey.equals("25")) {  respContent = "聊天唠嗑菜单项被点击!";  } else if (eventKey.equals("31")) {  respContent = "Q友圈菜单项被点击!";  } else if (eventKey.equals("32")) {  respContent = "电影排行榜菜单项被点击!";  } else if (eventKey.equals("33")) {  respContent = "幽默笑话菜单项被点击!";  }  } return respContent;}  }

View Code

三、参考资料

1.微信公众平台技术文档

2.柳峰—微信公众帐号开发教程第5篇-各种消息的接收与响应

转载于:https://www.cnblogs.com/shirui/p/7825481.html

Java微信公众平台开发_03_消息管理之被动回复消息相关推荐

  1. Java微信公众平台开发(一)--接入微信公众平台

    转载自崔用志博客:http://www.cuiyongzhi.com/ 前面几篇文章一直都在说微信公众平台的开发准备工作,那么从这篇开始我们就将正式的进入JAVA微信公众平台开发的整个流程,那么这篇我 ...

  2. Java微信公众平台开发(一)——接入微信公众平台

    前面几篇文章一直都在说微信公众平台的开发准备工作,那么从这篇开始我们就将正式的进入JAVA微信公众平台开发的整个流程,那么这篇我们开始聊聊如何将我们的服务端和微信公众平台对接! (一)接入流程解析 在 ...

  3. Java微信公众平台开发--番外篇,对GlobalConstants文件的补充

    转自:http://www.cuiyongzhi.com/post/63.html 之前发过一个[微信开发]系列性的文章,也引来了不少朋友观看和点评交流,可能我在写文章时有所疏忽,对部分文件给出的不是 ...

  4. 微信公众平台开发教程(二) 基本原理及消息接口

    微信公众平台开发教程(二) 基本原理及消息接口 一.基本原理 在开始做之前,大家可能对这个很感兴趣,但是又比较茫然.是不是很复杂?很难学啊? 其实恰恰相反,很简单.为了打消大家的顾虑,先简单介绍了微信 ...

  5. PHP7.0微信公众平台开发4: 实例一:接收普通消息和接收事件推送

    PHP7.0微信公众平台开发4: 实例一:接收普通消息和接收事件推送 1. API接口的声明 2. PHP脚本代码 3. 运行结果 由于消息类型有多钟,本文中,"接收普通消息"我以 ...

  6. 微信信息回复 java,微信公众平台开发中使用Java如何实现一个消息回复功能

    微信公众平台开发中使用Java如何实现一个消息回复功能 发布时间:2020-11-17 16:11:11 来源:亿速云 阅读:82 作者:Leah 本篇文章给大家分享的是有关微信公众平台开发中使用Ja ...

  7. Java微信公众平台开发(四)--回复消息的分类及实体的创建

    转自:http://www.cuiyongzhi.com/post/42.html 前面有说道对接收到微信服务器消息后对消息的分类,当时主要分为普通消息和事件消息,这里我们要讲述的是我们在给用户回复的 ...

  8. Java微信公众平台开发(十六)--微信网页授权(OAuth2.0授权)获取用户基本信息

    转自:http://www.cuiyongzhi.com/post/78.html 好长时间没有写文章了,主要是最近的工作和生活上的事情比较多而且繁琐,其实到现在我依然还是感觉有些迷茫,最后还是决定静 ...

  9. java微信公众平台开发接口_微信公众平台API的Java通讯实现

    微信公众平台 相信大家也不陌生,官方网站提供了一个简单的php程序Demo 因为微信平台采用HTTP方式承载微信的协议,而且不是双向通讯,也就是说只能由微信服务器主动请求我们的服务器.其实当你使用我开 ...

最新文章

  1. nio selector
  2. ionic 获取input的值
  3. SAP Business ByDesign云计算ERP软件
  4. WPF获取某控件的位置,也就是偏移量
  5. VTK:网格之AddCell
  6. python网络编程项目_Python网络编程攻略
  7. 全互联+杜比影音ThinkPad X1 Carbon 2019全新上市
  8. php爬取js对象,php如何用正则解析html中的js对象
  9. mysql5.6配置区分大小写
  10. Linex第三章第四章
  11. web前端开发 —— 一个对联效果
  12. 只有低价才是中国智能硬件的出路吗?
  13. 二、npm scripts
  14. UITableView分段加载数据
  15. JAVA 写入数据不覆盖_Java不覆盖原文件写入
  16. 【第17天】SQL进阶-查询优化- SHOW STATUS(SQL 小虚竹)
  17. iis mysql密码_mysql忘记root密码与root帐号被删除处理方法
  18. KSO-docker命令大全,基于Linux服务器CentOS7.5 安装docker
  19. VsCode插件整理
  20. Python读取Word文档内容

热门文章

  1. 【HTML、CSS、JS】注册页面实现(带验证码,密码强度验证,表格提交验证等)
  2. [Audacity][帮助手册][手册内容]认识Audacity
  3. 看steam教育之风带来创新与变革
  4. 成功的条件:高人指点、贵人相助、小人监督、个人奋斗
  5. python模拟登录163邮箱_Python实现模拟登录网易邮箱的方法示例
  6. 阔别母校35年!院士,履新北大!
  7. Winfrom实现微信扫码支付功能
  8. 数据分析Excel必备技能:数据透视表使用教程
  9. php插入数据含有特殊符号的处理方法
  10. linux 7进入目录的命令,centos7目录统计之du命令