前言
这次篇幅较长,不多比比,直接进入正文;
正文
在处理微信请求之前,我们要先分析一下微信的请求都有哪些,还有微信支持的我们的响应有哪些;打开随意一个公众号,我们可以看到我们要处理的信息一共有两种,一种是文字,图片,语音等这种的消息类型,一般的微信公众号不会去关注这些,特别是一些外包公司做的,这些基本上是点了之后没啥反映的。其实我认为这一部分用好了也能做出许多功能来;还有一种就是菜单或者关注,取关等等一些事件信息了,这一部分也是开发的重点,因为这里是允许跳转外链的,这样的话就可以使用网页来展示更多的元素信息;
首先,我们先分析一下用户发送请求的时候,微信服务器传递给我们的都有哪些数据:
这一部分实例官方文档中有,我就不贴出来浪费空间了,连接附上:点击打开链接
其实我在一开始写的时候,是把这一部分进行了封装,封装了每一种类型的Request请求。后来我发现那样的结果就是又徒增了一些代码,于是我果断放弃掉了这个方案,而是将请求数据封装成了一个map集合,使用get(key)的方式进行取值。
在返回的时候还是要把值封装一下的,这里我封装了Response响应对象,对应的是微信的响应信息;
首先所有的类都有一些共性,我们将这些共性先封装起来,封装一个BaseResponse
package com.wx.response; /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:响应消息基类 */ public class BaseResponse { // 接收方帐号(收到的OpenID) private String ToUserName; // 开发者微信号 private String FromUserName; // 消息创建时间 (整型) private long CreateTime; // 消息类型 private String MsgType; 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; } }
之后就是六种响应信息类的封装了,我在下边把代码一一贴出来,模型类没啥不好理解的,直接复制粘贴看一眼就好了;
package com.wx.response; /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:图片信息响应类 */ public class ImageResponse extends BaseResponse{ public ImageResponse(){ this.setMsgType("image"); } private Image Image; public Image getImage() { return Image; } public void setImage(Image image) { Image = image; } /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:图片模型类 */ public class Image{ private String MediaId; public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; } } }
package com.wx.response; /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:文本信息响应类 */ public class TextResponse extends BaseResponse{ public TextResponse(){ this.setMsgType("text"); } // 回复的消息内容 private String Content;
package com.wx.response; /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:音乐信息响应类 */ public class MusicResponse extends BaseResponse { public MusicResponse(){ this.setMsgType("music"); } // 音乐 private Music Music; public Music getMusic() { return Music; } public void setMusic(Music music) { Music = music; } /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:音乐信息模型 */ public class Music { // 音乐标题 private String Title; // 音乐描述 private String Description; // 音乐链接 private String MusicUrl; // 高质量音乐链接,WIFI环境优先使用该链接播放音乐 private String HQMusicUrl; // 缩略图的媒体id,通过上传多媒体文件得到的id private String ThumbMediaId; 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; } public String getThumbMediaId() { return ThumbMediaId; } public void setThumbMediaId(String thumbMediaId) { ThumbMediaId = thumbMediaId; } } }
public String getContent() { return Content; } public void setContent(String content) { Content = content; }}
package com.wx.response; import java.util.List; /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:图文消息响应类 */ public class NewsResponse extends BaseResponse { public NewsResponse(){ this.setMsgType("news"); } // 图文消息个数,限制为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; } /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:图文信息模型 */ public class Article { // 图文消息名称 private String Title; // 图文消息描述 private String Description; // 图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80 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() {
package com.wx.response; /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:视频信息响应类 */ public class VideoResponse extends BaseResponse { public VideoResponse(){ this.setMsgType("video"); } // 视频 private Video Video; public Video getVideo() { return Video; } public void setVideo(Video video) { Video = video; } /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:视频信息模型 */ public class Video { // 媒体文件id private String MediaId; // 缩略图的媒体id private String ThumbMediaId; public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; } public String getThumbMediaId() { return ThumbMediaId; } public void setThumbMediaId(String thumbMediaId) { ThumbMediaId = thumbMediaId; } } }
return null == Url ? "" : Url; } public void setUrl(String url) { Url = url; } }}
package com.wx.response; /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:语音信息响应类 */ public class VoiceResponse extends BaseResponse { public VoiceResponse(){ this.setMsgType("voice"); } // 语音 private Voice Voice; public Voice getVoice() { return Voice; } public void setVoice(Voice voice) { Voice = voice; } /** * User:Jiahengfei --> [email protected] * create by 2018年4月28日 from Eclipse. * describe:语音信息模型 */ public class Voice { // 媒体文件id private String MediaId; public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; } } }封装好响应信息之后,还要封装一些其他东西, 为开发做准备工作。
为此我们先封装一个消息工具类,用来处理一些繁琐的消息转换-->MessageUtil,这个类算是一个比较核心的类,因为后边所有与微信信息交互的地方都会用到这个类;
package com.wx.util; import java.io.InputStream; import java.io.Writer; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.core.util.QuickWriter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.XppDriver; import com.wx.response.BaseResponse; import com.wx.response.NewsResponse; import com.wx.response.TextResponse; /** * User:Jiahengfei --> [email protected] * create by 2018年5月1日 from Eclipse. * describe:消息处理工具类 */ public class MessageUtil { // 请求消息类型:文本 public static final String REQ_MESSAGE_TYPE_TEXT = "text"; // 请求消息类型:图片 public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; // 请求消息类型:语音 public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; // 请求消息类型:视频 public static final String REQ_MESSAGE_TYPE_VIDEO = "video"; // 请求消息类型:小视频 public static final String REQ_MESSAGE_TYPE_SHORTVIDEO = "shortvideo"; // 请求消息类型:地理位置 public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; // 请求消息类型:链接 public static final String REQ_MESSAGE_TYPE_LINK = "link"; // 请求消息类型:事件推送 public static final String REQ_MESSAGE_TYPE_EVENT = "event"; // 事件类型:subscribe(订阅) public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; // 事件类型:unsubscribe(取消订阅) public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; // 事件类型:scan(用户已关注时的扫描带参数二维码) public static final String EVENT_TYPE_SCAN = "scan"; // 事件类型:LOCATION(上报地理位置) public static final String EVENT_TYPE_LOCATION = "LOCATION"; // 事件类型:CLICK(自定义菜单) public static final String EVENT_TYPE_CLICK = "CLICK"; // 响应消息类型:文本 public static final String RESP_MESSAGE_TYPE_TEXT = "text"; // 响应消息类型:图片 public static final String RESP_MESSAGE_TYPE_IMAGE = "image"; // 响应消息类型:语音 public static final String RESP_MESSAGE_TYPE_VOICE = "voice"; // 响应消息类型:视频 public static final String RESP_MESSAGE_TYPE_VIDEO = "video"; // 响应消息类型:音乐 public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; // 响应消息类型:图文 public static final String RESP_MESSAGE_TYPE_NEWS = "news"; /** * 解析微信发来的请求(XML) * * @param request * @return Map<String, String> * @throws Exception */ @SuppressWarnings("unchecked") 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; } /** * 从一个流中解析一个数据 * @param inputStream * @return * @throws Exception */ public static Map<String, String> parseXml(InputStream inputStream) throws Exception { // 将解析结果存储在HashMap中 Map<String, String> map = new HashMap<String, String>(); // 读取输入流 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; } /** * 扩展xstream使其支持CDATA */ private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 对所有xml节点的转换都增加CDATA标记 boolean cdata = true; @SuppressWarnings("unchecked") public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); /** * 响应对象转换成Xml * @param textResponse 文本消息对象 * @return xml */ public static <T extends BaseResponse> String reponseTpXml(T t) { xstream.alias("xml", t.getClass()); return xstream.toXML(t); } /** * 图文消息对象转换成xml[特殊] * @param newsResponse 图文消息对象 * @return xml */ public static String newsResponseToXml(NewsResponse newsResponse) { xstream.alias("xml", newsResponse.getClass()); xstream.alias("item", new NewsResponse().new Article().getClass()); return xstream.toXML(newsResponse); } /** * 生成一个响应对象 * 注意:::::响应时,发送消息的名称和接受消息的名称是相反的 * @param map 接收的request集合 * @param t 相应对象的存储空间 * @return T */ public static <T extends BaseResponse> T mapTo(Map<String, String> map,T t){ t.setToUserName(map.get("FromUserName")); t.setFromUserName(map.get("ToUserName")); t.setCreateTime(new Date().getTime()); return t; } /** * User:Jiahengfei --> [email protected] * create by 2018年5月1日 from Eclipse. * describe:获取一些预定义好的消息对象 */ public static class Default{ /** * 文本消息 * @param content 回复的消息内容 */ public static String text(Map<String, String> map,String content){ TextResponse text = mapTo(map, new TextResponse()); text.setContent(content); return reponseTpXml(text); } } }这里需要两个jar包,一个是dom4j,还有一个是xstream,下载连接附上了,卖点积分,没积分下载的话直接联系我就好了,我直接发给你; 下载dom4j+xstream
其他的封装用到的时候再讲,因为开发终归是要用json的,我一直用的都是fastjson,下面附上了一个json解析工具类,我一直在用;
package com.wx.util; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Summarize:json解析工具类 * User:贾恒飞 * Date:2018/3/31 * Time:2:08 * Email:[email protected] * Created with AndroidStudio3.0 */ public class Json { /** * 解析JsonObject数据 * * @param jsonString * Json格式字符串 * @param cls * 封装类 * */ public static <T> T obj(String jsonString, Class<T> cls) { T t = null; try { t = JSON.parseObject(jsonString, cls); } catch (Exception e) { e.printStackTrace(); } return t; } /** * 解析JsonArray数据 * * @param jsonString * @param cls * @return */ public static <T> List<T> array(String jsonString, Class<T> cls) { List<T> list = new ArrayList<T>(); try { list = JSON.parseArray(jsonString, cls); } catch (Exception e) { e.printStackTrace(); } return list; } /** * 解析JsonArray数据,返回Map类型的List * * @param jsonString * @return */ public static List<Map<String, Object>> maps(String jsonString) { List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); try { list = JSON.parseObject(jsonString,new TypeReference<List<Map<String, Object>>>() {}); } catch (Exception e) { e.printStackTrace(); } return list; } /** * json to map * @param jsonStr * @return */ @SuppressWarnings("unchecked") public static Map<String,Object> map(String jsonStr){ return JSON.parseObject(jsonStr, Map.class); } /** * 对象解析成Json数据 * @param t 对象 * @param <T> 类型 * @return json字符串 */ public static <T> String toJson(T t){ String json = ""; try { json = JSON.toJSONString(t); } catch (Exception e) { e.printStackTrace(); } return json; } }