公众号给微信服务器响应数据
一、本节要点
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 Code4.消息对象的封装
根据官方文档消息的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 Code5.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 Code2.HTTP请求工具类—HttpHelper.javapackage 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();//2000httpGet.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 Code3、接收消息的封装
3.1 消息基类—BaseMessagepackage 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 Code3.2 文本消息—TextMessagepackage 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 Code3.3 图片消息—ImageMessagepackage 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 Code3.4 链接消息—LinkMessagepackage 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 Code3.5 地理位置消息—LocationMessagepackage 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 Code3.6 音频消息—VoiceMessagepackage 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 Code4. 回复消息的封装
4.1 消息基类—BaseMessagepackage 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 Code4.2 文本消息—TextMessagepackage 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 Code4.3 音乐消息—MusicMessage
Musicpackage 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
MusicMessagepackage 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 Code4.4 图文消息—NewsMessage
Articlepackage 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
NewsMessagepackage 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 Code5.接收微信消息和事件—WeiXinServlet.javapackage 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若校验成功,则原样返回 echoStrout = response.getWriter(); out.print(sEchoStr); } catch (AesException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.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 Code6.被动回复消息业务类—ReplyMessageService.javapackage 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 blocke.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;} }
公众号给微信服务器响应数据相关推荐
- 微信公众号获取微信服务器IP地址
如果公众号基于安全等考虑,需要获知微信服务器的IP地址列表,以便进行相关限制,可以通过该接口获得微信服务器IP地址列表或者IP网段信息. http请求方式: GEThttps://api.weixin ...
- 微信公众号python_wechat: 微信 Python SDK,支持微信公众号以及企业号的上行消息及 OAuth 接口...
微信公众号Python-SDK 本SDK支持微信公众号以及企业号的上行消息及OAuth接口.本文档及SDK假设使用者已经具备微信公众号开发的基础知识,及有能力通过微信公众号.企业号的文档来查找相关的接 ...
- 关于开发微信公众号获取手机用户运动数据的功能实现思路
一.前沿研究 微信公众号开发文档,浏览后没有任何关于获取微信运动数据的接口 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp144 ...
- 公众号自动回复需要服务器,关于微信公众号消息自动回复5秒限制问题的解决方案...
一般申请了公众号的想要快速部署投入应用的,可以使用微信公众号平台自带的消息自动回复等功能,但是不能对用户发来的消息进行处理,所以我们会使用程序对接微信公众号进行微信公众号开发. 于是大家可能都会遇到这 ...
- 微信公众号数据2019_嘀!请查收丨哈信息2019年官方微信公众号、官方微博年度数据报告...
哈尔滨信息工程学院2019年官方微信公众号&官方微博年度数据报告 现已出炉! 一直关注咱们两微的各位亲,请查收! 微信公众号部分 关键词:价值连城:神仙操作 微博部分 关键词:好评如潮:时代建 ...
- 淘宝客CMS,微信公众号,微信淘客机器人
淘宝客CMS,微信公众号,微信淘客机器人一站式淘客导购解决方案 先来写个序 楼主程序员,两耳不闻窗外事,一心只知敲代码,2019年前没接触过淘客,也不懂淘客是什么? 后来有个朋友给我提到,说他在做淘客 ...
- 微信公众号给微信用户推送信息 模板信息
该功能主要是用于利用微信公众号向微信用户推送与用户相关的信息,在开发此功能之前需要获取到微信公众号的access_token,以及微信公众号的模板id 为了保证用户不受到骚扰,在开发者出现需要主动提醒 ...
- springboot+h5页面+微信公众号获取微信用户信息
springboot项目,h5页面通过微信公众号获取微信用户信息 最近本人有一个项目需求,微信公众号里点击一个菜单进入一个商城购物系统. 对于在微信公众号还是小白的我来说难度有点大,但是做完后发现也就 ...
- 【微信公众号】微信集成功能--扫描二维码完成用户登录操作
目录 需求来源 实现思路 1.进入登录页面,生成微信公众号的临时二维码: 2.用户通过微信扫一扫二维码: 3.登录页面定时查询扫码结果: 代码实现(基于Laravel框架前后端混合) HTML PHP ...
最新文章
- 微型计算机系统外文,微型计算机控系统(单片机控制系统) 毕业论文外文翻译.doc...
- 有关EUV光刻机,你需要知道这些
- PAT—— 害死人不偿命的(3n+1)猜想 (1001)
- 女性自我的迷宫:看EMI的人体自拍
- android中文转字节数组,如何将Android中的byte []转换为C中的uint8_T数组?
- 2013江苏计算机二级vfp试题,2013年3月全国计算机二级VFP真题
- rust 官服指令_【RUST】每个RUST玩家都需要的十个指令
- GPL侵权诉讼被驳回,Linux之父Torvalds又要发飙了!
- linux wsgi,linux中wsgi的详解(企业级)
- signature=fd45b8c9a90eebce5d855f07302ab4ee,Private Use Area
- 手把手教你DosBox的配置(附下载资源)
- 微擎小程序PHP,微擎配置小程序教程
- Mycat分库分表优缺点分析
- Python 保存图片的两种方法
- 【Unity Shader 描边效果_案例分享】
- 一种适用于主流工业机器人的简单的码垛算法
- 记一次错,数据库报syntax
- H.264码率控制算法研究及JM相应代码分析(二)
- 做7秒动画赢13W大奖?总奖池超80W、国内最火爆的3D渲染动画创作大赛开始报名!
- 【百日刷题计划 第一天】——熟悉语法 语法基础题