Java微信公众平台开发_03_消息管理之被动回复消息
Posted shirayner
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java微信公众平台开发_03_消息管理之被动回复消息相关的知识,希望对你有一定的参考价值。
GitHub源码:https://github.com/shirayner/weixin_gz
一、本节要点
1.回调url
上一节,我们启用服务器配置的时候,填写了一个服务器地址(url),如下图,这个url就是回调url,是开发者用来接收微信消息和事件的接口URL 。也就是说,用户在微信公众号中发送的消息会被推送到这个回调url,而我们可以接收用户的消息,并进行回复。
2.被动回复消息的流程
官方文档:
我们在上一节中设置的消息加解密方式是安全模式。因此在用户发给公众号的消息(接收消息)以及公众号被动回复用户消息(回复消息)都会加密,
流程:
用户发送消息之后,微信服务器将消息传递给 第三方服务器,第三方服务器接收到消息后,再对消息做出相应的回复消息。
接收消息:需先从request请求对象的输入流中获取请求参数和已加密的请求消息,再对已加密的请求消息进行解密操作,即可获得明文。
然后就行对明文消息的业务处理了。
回复消息:封装好回复消息后,需先对回复消息进行加密,获得已已加密消息,然后再通过http请求调用被动回复消息的接口,来发送消息。
3.被动回复消息加解密
3.1接收消息的 解密
(1)从请求的输入流中获取加密的请求消息
//1.获取加密的请求消息:使用输入流获得加密请求消息postData ServletInputStream in = request.getInputStream(); BufferedReader reader =new BufferedReader(new InputStreamReader(in)); String tempStr=""; //作为输出字符串的临时串,用于判断是否读取完毕 while(null!=(tempStr=reader.readLine())){ postData+=tempStr; } logger.info("postData:"+postData);
(2)对加密的请求消息进行解密获得明文
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID); result=wxcpt.DecryptMsg(msgSignature, timeStamp, nonce, postData);
(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);
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; }
5.2 将文本消息转成xml字符串
/** * 2.文本消息对象转换成xml * * @param textMessage 文本消息对象 * @return xml */ public static String textMessageToXml(TextMessage textMessage) { xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); }
二、代码实现
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"; }
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转换为 JSONObject 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; } /** 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 设置数据传输格式-json httpPost.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 设置数据传输格式-json httpPost.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 响应 *以上是关于Java微信公众平台开发_03_消息管理之被动回复消息的主要内容,如果未能解决你的问题,请参考以下文章