关注/取消关注事件
用户在关注与取消关注公众号时,微信会把这个事件推送到开发者填写的URL。方便开发者给用户下发欢迎消息或者做帐号的解绑。
微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。
关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。
假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。
推送XML数据包示例:
<xml><ToUserName>< ![CDATA[toUser] ]></ToUserName><FromUserName>< ![CDATA[FromUser] ]></FromUserName><CreateTime>123456789</CreateTime><MsgType>< ![CDATA[event] ]></MsgType><Event>< ![CDATA[subscribe] ]></Event></xml>
参数说明:
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,event |
Event | 事件类型,subscribe(订阅)、unsubscribe(取消订阅) |
回复图文消息
<xml><ToUserName>< ![CDATA[toUser] ]></ToUserName><FromUserName>< ![CDATA[fromUser] ]></FromUserName><CreateTime>12345678</CreateTime><MsgType>< ![CDATA[news] ]></MsgType><ArticleCount>2</ArticleCount><Articles><item><Title>< ![CDATA[title1] ]></Title> <Description>< ![CDATA[description1] ]></Description><PicUrl>< ![CDATA[picurl] ]></PicUrl><Url>< ![CDATA[url] ]></Url></item><item><Title>< ![CDATA[title] ]></Title><Description>< ![CDATA[description] ]></Description><PicUrl>< ![CDATA[picurl] ]></PicUrl><Url>< ![CDATA[url] ]></Url></item></Articles></xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | news |
ArticleCount | 是 | 图文消息个数,限制为8条以内 |
Articles | 是 | 多条图文消息信息,默认第一个item为大图,注意,如果图文数超过8,则将会无响应 |
Title | 是 | 图文消息标题 |
Description | 是 | 图文消息描述 |
PicUrl | 是 | 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200 |
Url | 是 | 点击图文消息跳转链接 |
News.java
public class News { private String Title;//图文标题 private String Description;//图文描述 private String PicUrl;//图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200 private String Url;//点击图文消息跳转链接 public News(String title, String description, String picUrl, String url) { this.Title = title; this.Description = description; this.PicUrl = picUrl; this.Url = url; } public News(){super();} public String getTitle() { return Title; } public void setTitle(String title) { this.Title = title; } public String getDescription() { return Description; } public void setDescription(String description) { this.Description = description; } public String getPicUrl() { return PicUrl; } public void setPicUrl(String picUrl) { this.PicUrl = picUrl; } public String getUrl() { return Url; } public void setUrl(String url) { this.Url = url; } }
NewsMessage.java
public class NewsMessage extends BaseMessage { private int ArticleCount;//图文消息个数,限制为8条以内 private List<News> Articles;//多条图文消息信息,默认第一个item为大图,注意,如果图文数超过8,则将会无响应 public NewsMessage(String toUserName, String fromUserName, Long createTime, String msgType, Integer articleCount, List<News> articles) { super(toUserName, fromUserName, createTime, msgType); this.ArticleCount = articleCount; this.Articles = articles; } public NewsMessage() { super(); } public int getArticleCount() { return ArticleCount; } public void setArticleCount(int articleCount) { this.ArticleCount = articleCount; } public List<News> getArticles() { return Articles; } public void setArticles(List<News> articles) { this.Articles = articles; } }
初始化图文消息:
/** * 初始化图文消息 */ public static String initNewsMessage(String toUSerName, String fromUserName) { List<News> newsList = new ArrayList<News>(); NewsMessage newsMessage = new NewsMessage(); //组建一条图文↓ ↓ ↓ News newsItem = new News(); newsItem.setTitle("欢迎关注我的公众号"); newsItem.setDescription("进行操作之前请先注册!"); newsItem.setPicUrl(WXConstants.BASE_SERVER + "/image/wx/login_article_cover.png"); newsItem.setUrl("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WXConstants.APPID + "&redirect_uri=" + WXConstants.BASE_SERVER + "/wxuser/toRegister&response_type=code&scope=snsapi_base&state=BINDFACE#wechat_redirect"); newsList.add(newsItem); //组装图文消息相关信息 newsMessage.setToUserName(fromUserName); newsMessage.setFromUserName(toUSerName); newsMessage.setCreateTime(new Date().getTime()); newsMessage.setMsgType(WXConstants.MESSAGE_NEWS); newsMessage.setArticles(newsList); newsMessage.setArticleCount(newsList.size()); //调用newsMessageToXml将图文消息转化为XML结构并返回 return MessageUtil.newsMessageToXml(newsMessage); } /** * 图文消息转XML结构方法 */ public static String newsMessageToXml(NewsMessage message) { XStream xs = new XStream(); //由于转换后xml根节点默认为class类,需转化为<xml> xs.alias("xml", message.getClass()); xs.alias("item", new News().getClass()); return xs.toXML(message); }
测试关注:
@Controller @RequestMapping("/wechat") public class WxController { private final static String MEDIATYPE_CHARSET_JSON_UTF8 = MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8"; private static Logger log = LoggerFactory.getLogger(WxController.class); @RequestMapping(value = "/chat", method = {RequestMethod.GET, RequestMethod.POST}, produces = MEDIATYPE_CHARSET_JSON_UTF8) public void get(HttpServletRequest request, HttpServletResponse response) throws Exception { //如果为get请求,则为开发者模式验证 if ("get".equals(request.getMethod().toLowerCase())) { doGet();//在开发者模式验证中已处理,在此省略 } else { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); try { Map<String, String> map = MessageUtil.xmlToMap(request); String ToUserName = map.get("ToUserName"); String FromUserName = map.get("FromUserName"); request.getSession().setAttribute("openid",FromUserName); String CreateTime = map.get("CreateTime"); String MsgType = map.get("MsgType"); String message = null; if (MsgType.equals(WXConstants.MESSAGE_EVENT)) { //从集合中,获取是哪一种事件传入 String eventType = map.get("Event"); if (eventType.equals(WXConstants.MESSAGE_SUBSCRIBE)) { message = MessageUtil.initNewsMessage(ToUserName, FromUserName); } } out.print(message); //返回转换后的XML字符串 } catch (DocumentException e) { e.printStackTrace(); } out.close(); } } }
关注测试号,弹出如下结果: