1、开发接入
(1)更改入门教程中的Controller: 注(验证是属于GET,然而交互是属于POST)
@RequestMapping(value = "/getMsg", method = {RequestMethod.GET, RequestMethod.POST}) public void getMsg(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); boolean isGet = req.getMethod().toLowerCase().equals("get"); if (isGet) { String signature = req.getParameter("signature"); String timestamp = req.getParameter("timestamp"); String nonce = req.getParameter("nonce"); String echostr = req.getParameter("echostr"); if(CheckUtil.checkSignature(signature, timestamp, nonce)){ //如果校验成功,将得到的随机字符串原路返回 resp.getWriter().write(echostr); }
}else{ try { // 接收消息并返回消息 postMsg(req, resp); } catch (IOException e) { e.printStackTrace(); } }
(2)消息回复的基本准备:
1、主要开发包:xstream-1.4.8.jar
maven工程可导入依赖
<dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.8</version> </dependency>
2、添加各种工具类
@XStreamAlias("xml") public class BaseMessage { /** * 接收方帐号(收到的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; } }
/** * 文本实体类 * @author xzl * */ @XStreamAlias("xml") public class TextMeaasge extends BaseMessage { /** * 文本消息内容 */ private String Content; public String getContent() { return Content; } public void setContent(String content) { Content = content; } }
public class WeiXinUserInfo { private int subscribe;// 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。 private String openid; // 用户的标识,对当前公众号唯一 private String nickname;// 用户的昵称 private int sex;// 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 private String city;// 用户所在城市 private String country;// 用户所在国家 private String province;// 用户所在省份 private String language;// 用户的语言,简体中文为zh_CN private String headimgurl;// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。 private String subscribe_time;// 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间 private String unionid;// 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。 private String remark;// 公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注 private int groupid;// 用户所在的分组ID(兼容旧的用户分组接口) private String tagid_list; // 用户被打上的标签ID列表 public int getSubscribe() { return subscribe; } public void setSubscribe(int subscribe) { this.subscribe = subscribe; } public String getOpenid() { return openid; } public void setOpenid(String openid) { this.openid = openid; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } public String getHeadimgurl() { return headimgurl; } public void setHeadimgurl(String headimgurl) { this.headimgurl = headimgurl; } public String getSubscribe_time() { return subscribe_time; } public void setSubscribe_time(String subscribe_time) { this.subscribe_time = subscribe_time; } public String getUnionid() { return unionid; } public void setUnionid(String unionid) { this.unionid = unionid; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public int getGroupid() { return groupid; } public void setGroupid(int groupid) { this.groupid = groupid; } public String getTagid_list() { return tagid_list; } public void setTagid_list(String tagid_list) { this.tagid_list = tagid_list; } }
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转换成Map集合 */ @SuppressWarnings("unchecked") public static Map<String, String>xmlToMap(HttpServletRequest request) throws IOException, DocumentException{ Map<String, String> map = new HashMap<String, String>(); SAXReader reader = new SAXReader(); // 使用dom4j解析xml InputStream ins = request.getInputStream(); // 从request中获取输入流 Document doc = reader.read(ins); Element root = doc.getRootElement(); // 获取根元素 List<Element> list = root.elements(); // 获取所有节点 for (Element e : list) { map.put(e.getName(), e.getText()); System.out.println(e.getName() + "--->" + e.getText()); } ins.close(); return map; } /** * 对象到xml的处理 */ private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 对所有xml节点的转换都增加CDATA标记 boolean cdata = true; @SuppressWarnings("rawtypes") 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 textMessage 文本消息对象 * @return xml */ public static String messageToXml(TextMeaasge textMessage) { xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); } /** * 图文消息对象转换成xml * * @param newsMessage 图文消息对象 * @return xml */ public static String messageToXml(NewsMessage newsMessage) { xstream.alias("xml", newsMessage.getClass()); xstream.alias("item", new Article().getClass()); return xstream.toXML(newsMessage); } /** * 图片消息对象转换成xml * * @param imageMessage 图片消息对象 * @return xml */ public static String messageToXml(ImageMessage imageMessage) { xstream.alias("xml", imageMessage.getClass()); return xstream.toXML(imageMessage); } /** * 语音消息对象转换成xml * * @param voiceMessage 语音消息对象 * @return xml */ public static String messageToXml(VoiceMessage voiceMessage) { xstream.alias("xml", voiceMessage.getClass()); return xstream.toXML(voiceMessage); } /** * 视频消息对象转换成xml * * @param videoMessage 视频消息对象 * @return xml */ public static String messageToXml(VideoMessage videoMessage) { xstream.alias("xml", videoMessage.getClass()); return xstream.toXML(videoMessage); } /** * 音乐消息对象转换成xml * * @param musicMessage 音乐消息对象 * @return xml */ public static String messageToXml(MusicMessage musicMessage) { xstream.alias("xml", musicMessage.getClass()); return xstream.toXML(musicMessage); } }
/** * 列表图文信息 * @author xzl * */ @XStreamAlias("xml") 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; } }
/** * 图片消息 * @author xzl * */ @XStreamAlias("xml") public class ImageMessage extends BaseMessage { // 图片链接 private String PicUrl; //图片消息媒体id private String MediaId; public String getPicUrl() { return PicUrl; } public void setPicUrl(String picUrl) { PicUrl = picUrl; } public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; } }
/** * 音频消息 * @author xzl * */ @XStreamAlias("xml") 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; } }
/** * 视频消息 * @author xzl * */ @XStreamAlias("xml") public class VideoMessage extends BaseMessage { // 媒体ID private String MediaId; // 语音格式 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; } }
/** * 音乐消息 * @author xzl * */ @XStreamAlias("xml") public class MusicMessage extends BaseMessage { // 音乐 private Music Music; public Music getMusic() { return Music; } public void setMusic(Music music) { Music = music; } }
@XStreamAlias("xml") public class Music extends BaseMessage { // 音乐名称 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 hQMusicUrl) { HQMusicUrl = hQMusicUrl; } public String getThumbMediaId() { return ThumbMediaId; } public void setThumbMediaId(String thumbMediaId) { ThumbMediaId = thumbMediaId; } }
@XStreamAlias("xml") public class Article extends BaseMessage { /** * 消息名称 */ 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 Description; } public void setDescription(String description) { Description = description; } public String getPicUrl() { return PicUrl; } public void setPicUrl(String picUrl) { PicUrl = picUrl; } public String getUrl() { return null == Url ? "" : Url; } public void setUrl(String url) { Url = url; } }
以上都是关于各种消息的工具类,当然,肯定还有我没有总结到的,但,知识有限,差不多就得了,大概常用的应该就是这些。。
下面还有一些需要的类:
/** * 微信通用接口凭证 * @author xzl * */ public class AccessToken { // 获取到的凭证 private String accessToken; // 凭证有效时间,单位:秒 private int expiresin; public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public int getExpiresin() { return expiresin; } public void setExpiresin(int expiresin) { this.expiresin = expiresin; } }
/** * 公众平台通用接口工具类 * @author xzl * */ public class WeixinUtil { private static Logger log = LoggerFactory.getLogger(WeixinUtil.class); /** * 获取access_token * * @param appid 凭证 * @param appsecret 密钥 * @return */ public static AccessToken getAccessToken(String appid, String appsecret) { // 获取access_token的接口地址(GET) 限200(次/天) final String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; AccessToken accessToken = null; String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret); JSONObject jsonObject = httpRequest(requestUrl, "GET", null); // 如果请求成功 if (null != jsonObject) { try { accessToken = new AccessToken(); accessToken.setAccessToken(jsonObject.getString("access_token")); accessToken.setExpiresin(jsonObject.getInt("expires_in")); } catch (JSONException e) { accessToken = null; // 获取token失败 log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return accessToken; } /** * URL编码(utf-8) * * @param source * @return */ public static String urlEncodeUTF8(String source) { String result = source; try { result = java.net.URLEncoder.encode(source, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; } /** * 根据内容类型判断文件扩展名 * * @param contentType 内容类型 * @return */ public static String getFileExt(String contentType) { String fileExt = ""; if ("image/jpeg".equals(contentType)) fileExt = ".jpg"; else if ("audio/mpeg".equals(contentType)) fileExt = ".mp3"; else if ("audio/amr".equals(contentType)) fileExt = ".amr"; else if ("video/mp4".equals(contentType)) fileExt = ".mp4"; else if ("video/mpeg4".equals(contentType)) fileExt = ".mp4"; return fileExt; } /** * 获取用户信息 * * @param accessToken 接口访问凭证 * @param openId 用户标识 * @return WeixinUserInfo */ public static WeiXinUserInfo getUserInfo(String accessToken, String openId) { WeiXinUserInfo weixinUserInfo = null; // 拼接请求地址 String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID"; requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId); // 获取用户信息 JSONObject jsonObject = WeixinUtil.httpRequest(requestUrl, "GET", null); if (null != jsonObject) { try { weixinUserInfo = new WeiXinUserInfo(); // 用户的标识 weixinUserInfo.setOpenid(jsonObject.getString("openid")); // 关注状态(1是关注,0是未关注),未关注时获取不到其余信息 weixinUserInfo.setSubscribe(jsonObject.getInt("subscribe")); // 用户关注时间 weixinUserInfo.setSubscribe_time(jsonObject.getString("subscribe_time")); // 昵称 weixinUserInfo.setNickname(jsonObject.getString("nickname")); // 用户的性别(1是男性,2是女性,0是未知) weixinUserInfo.setSex(jsonObject.getInt("sex")); // 用户所在国家 weixinUserInfo.setCountry(jsonObject.getString("country")); // 用户所在省份 weixinUserInfo.setProvince(jsonObject.getString("province")); // 用户所在城市 weixinUserInfo.setCity(jsonObject.getString("city")); // 用户的语言,简体中文为zh_CN weixinUserInfo.setLanguage(jsonObject.getString("language")); // 用户头像 weixinUserInfo.setHeadimgurl(jsonObject.getString("headimgurl")); } catch (Exception e) { if (0 == weixinUserInfo.getSubscribe()) { log.error("用户{}已取消关注", weixinUserInfo.getOpenid()); } else { int errorCode = jsonObject.getInt("errcode"); String errorMsg = jsonObject.getString("errmsg"); log.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode, errorMsg); } } } return weixinUserInfo; } }
这里主要就是用来获取AccessToken和用户信息
下面继续。。。
如果仔细观看了会发现上面更改的Controller的方法里面少了多了个postMsg这个方法,,,
贴上
public void postMsg(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Map<String, String> map = MessageUtil.xmlToMap(request); String toUserName = map.get("ToUserName"); String fromUserName = map.get("FromUserName"); String msgType = map.get("MsgType"); String content = map.get("Content"); TextMeaasge text = new TextMeaasge(); // 发送和回复是反向的 text.setToUserName(fromUserName); text.setFromUserName(toUserName); text.setCreateTime(new Date().getTime()); text.setMsgType(MessageUtil.REQ_MESSAGE_TYPE_TEXT); //获取用户信息 WeiXinUserInfo weiXinUserInfo = WeixinUtil.getUserInfo(WeixinUtil.getAccessToken(appid, appSecret).getAccessToken(),fromUserName); text.setContent("输入不详!!!请重新输入!!!"); String respMessage = MessageUtil.messageToXml(text); response.getWriter().write(respMessage);// 将回应发送给微信服务器 } catch (Exception e) { e.printStackTrace(); } }
注:其中的appid和appSecret是微信公众平台中提供的
这样就有消息返回了,但是,,这样的话,不管用户输入什么都只是返回 “输入不详”,所以这并不是我们想要的
继续改。。
public void postMsg(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Map<String, String> map = MessageUtil.xmlToMap(request); String toUserName = map.get("ToUserName"); String fromUserName = map.get("FromUserName"); String msgType = map.get("MsgType"); String content = map.get("Content"); TextMeaasge text = new TextMeaasge(); // 发送和回复是反向的 text.setToUserName(fromUserName); text.setFromUserName(toUserName); text.setCreateTime(new Date().getTime()); text.setMsgType(MessageUtil.REQ_MESSAGE_TYPE_TEXT); text.setFuncFlag(0); WeiXinUserInfo weiXinUserInfo = WeixinUtil.getUserInfo(WeixinUtil.getAccessToken(appid, appSecret).getAccessToken(),fromUserName); text.setContent("输入不详!!!请重新输入!!!"); String respMessage = MessageUtil.messageToXml(text); // 文本消息 if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { // 创建图文消息 NewsMessage newsMessage = new NewsMessage(); newsMessage.setToUserName(fromUserName); newsMessage.setFromUserName(toUserName); newsMessage.setCreateTime(new Date().getTime()); newsMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_NEWS); List<Article> articleList = new ArrayList<Article>(); // 指定消息回复 if ("1".equals(content)) { text.setContent("今天的天气真不错!"); respMessage = MessageUtil.messageToXml(text); } //单图文消息 else if("2".equals(content)){ Article article = new Article(); article.setTitle("微信公众帐号开发教程Java版"); article.setDescription("第一张图片"); article.setPicUrl("http://pic.qiantucdn.com/58pic/26/10/40/58c04e46c2ffa_1024.jpg!/fw/780/watermark/url/L3dhdGVybWFyay12MS4zLnBuZw==/align/center"); article.setUrl("http://www.cnblogs.com/x-99/"); articleList.add(article); // 设置图文消息个数 newsMessage.setArticleCount(articleList.size()); // 设置图文消息包含的图文集合 newsMessage.setArticles(articleList); // 将图文消息对象转换成xml字符串 respMessage = MessageUtil.messageToXml(newsMessage); } //多图文消息 else if("3".equals(content)){ Article article1 = new Article(); article1.setTitle("微信公众帐号开发教程Java版"); article1.setDescription(""); article1.setPicUrl("http://pic.qiantucdn.com/58pic/26/10/40/58c04e46c2ffa_1024.jpg!/fw/780/watermark/url/L3dhdGVybWFyay12MS4zLnBuZw==/align/center"); article1.setUrl("http://www.cnblogs.com/x-99/"); Article article2 = new Article(); article2.setTitle("微信公众帐号开发教程.NET版"); article2.setDescription(""); article2.setPicUrl("http://pic.qiantucdn.com/58pic/26/10/40/58c04e46c2ffa_1024.jpg!/fw/780/watermark/url/L3dhdGVybWFyay12MS4zLnBuZw==/align/center"); article2.setUrl("http://www.cnblogs.com/x-99/"); Article article3 = new Article(); article3.setTitle("微信公众帐号开发教程C版"); article3.setDescription(""); article3.setPicUrl("http://pic.qiantucdn.com/58pic/26/10/40/58c04e46c2ffa_1024.jpg!/fw/780/watermark/url/L3dhdGVybWFyay12MS4zLnBuZw==/align/center"); article3.setUrl("http://www.cnblogs.com/x-99/"); articleList.add(article1); articleList.add(article2); articleList.add(article3); // 设置图文消息个数 newsMessage.setArticleCount(articleList.size()); // 设置图文消息包含的图文集合 newsMessage.setArticles(articleList); // 将图文消息对象转换成xml字符串 respMessage = MessageUtil.messageToXml(newsMessage); } } System.out.println(respMessage); response.getWriter().write(respMessage);// 将回应发送给微信服务器 } catch (Exception e) { e.printStackTrace(); } }
大功告成!这样就可以根据用户输入的信息来响应不同的结果
当然,用户不一定就是发送文本消息,这个你也可以多加几个判断OK了。
以上就是我个人的总结,希望能够帮到大家、、、
下篇来讲述公众号的、、、自定义菜单栏