java微信第三方平台全网发布(三)
在java微信第三方平台开发(二)中写了授权事件的处理,并且第三方平台代公众号发起网页授权,获取用户信息和发红包等基本业务。接下来代公众号处理消息和事件。这时候就需要用到在开发者资料中填写的公众号消息与事件接受的URL了
一、接受处理微信消息和事件信息
URL地址
格式为:http://xxxx.com/b/weixin2/APPID/callback
当用户在发送文本信息,或者取消关注,上报地理位置等一些列操作时,微信会向该接口地址推送一段加密的xml文件。需要解密后才能获取详细消息类型和事件类型。(相关解密的操作可以查看:java微信第三方平台开发(二))相关代码:
@RequestMapping(value="/{appid}/callback",method={RequestMethod.GET,RequestMethod.POST})public void callBackEvent(@PathVariable String appid,HttpServletResponse response,HttpServletRequest request){try {Log.logger.info(appid+"进入callback+++++++++++++++++++++++++++++++++");weChatThridService.handleMessage(request,response);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}
- 接受消息事件的xml
微信把消息事件分为2种类型:
1、普通消息
文本消息、图片消息、视频消息、链接消息等等
解密后的xml文件的格式大体为:
<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[this is a test]]></Content><MsgId>1234567890123456</MsgId></xml>
ToUserName:公众号的原始ID,接收方
FromUserName:用户的openID,发送方
CreateTime:发送的时间
MsgType:类型,文本时text,如果是图片消息是image
Content: 内容
关于普通消息更多的类型介绍可以参考微信开发文档:
https://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html
2、事件消息
关注取消关注、上报地理位置等等一系列事件消息。
解密后的xml文件:
<xml>
<ToUserName><![CDATA[gh_3442d2]]></ToUserName>
<FromUserName><![CDATA[o1jDIIu0cljZwTOhmrX1q_3zQ]]></FromUserName>
<CreateTime>1479953039</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[LOCATION]]></Event>
<Latitude>44.541485</Latitude>
<Longitude>125.696594</Longitude>
<Precision>40.000000</Precision>
</xml>
我们发现前3项是相同的,但是第四个虽然Element相同都是MsgType,但是内容却不相同,后续可以根据event和text的不同,做出不同的处理。
处理消息xml
既然是处理消息那当然少不了解密xml,然后写一个公用的方法,根据解密后的msgtype的不同来进一步实现不同的业务逻辑(中间可能包括耗时操作,比如用户上报的地理位置的经纬想存在数据库中,方便绘制用户的移动轨迹了(这是有点坏- 。-)),最后需要返回给微信响应。说到给微信响应的这个就比较有复杂了,微信在推送xml后5s内收不到响应后,会断开连接,并且重新发起请求,总共重试三次,如果不能开发者不能确保自己服务器能响应的话可以直接回复空字符串。如果有业务要求,可以调用客服高级接口回复用户内容。内容可以自己定义。但是不回复的话,微信会推送重复的xml进来,这时就需要排重啦。一个static arraylist< string>(1000),当超过10000时清除key。
/*** 接收消息 排重* @param type 消息的类型: 1 文本 0 事件 * @param msgId 文本的内容* @param FromUserName 消息的发送方,此处为openId* @param CreateTime 消息创建的时间* @return false 重复的 true 不重复 */public boolean messageExcludeRepeat(int type,String msgId,String FromUserName,String CreateTime ){//当数量大于10000时,删除if(excludeRepeatList.size()>=10000){excludeRepeatList.clear();}if(type==1){String key=msgId+FromUserName+CreateTime;//判断该文本消息是否是重复的if(excludeRepeatList.contains(key)){return false;}excludeRepeatList.add(key);}else{String key=FromUserName+CreateTime;//判断该事件消息是否是重复的if(excludeRepeatList.contains(key)){return false;}excludeRepeatList.add(key);}return true;}
当我们有耗时操作时,不能在5s内回复微信的时候。解决办法有两种:
(1)、先回复微信空串,后续再24小时内利用客服api回复用户,这种办法需要公众号有高级接口权限。
(2)、利用selvet3.0的新特性AsyncContext来实现,微信第一个5s内如果没能完成耗时操作,直接return,但是连接不会断开,重新重request中解析xml。直到完成操作,在返回给微信响应。相关AsyncContext的用法,后续介绍。
下面就第一种方法结合全网发布的必要的规范流程详细写写,包括回复微信的响应内容。
二、全网发布详细流程
微信要求开发者在接到xml文件后,经过一系列处理后,返回给为微信服务器响应。响应的内容为xml格式的文件,并且是加密的xml文件。对于不同的消息类型有不同回复格式,接下来要做的就是,根据不同的msgtype一方面完成我们的业务,另一方面封装好不同的xml文件加密,发送给微信服务器。
- 判断事件类型
根据msgtype来判断
/*** 处理全网检测发布,回复微信xml* * @param request* @param response* @throws Exception */public void handleMessage(HttpServletRequest request,HttpServletResponse response) throws Exception {String msgSignature = request.getParameter("msg_signature"); if(!StringUtils.isNotBlank(msgSignature)){return;}String timestamp=request.getParameter("timestamp");String encrypt_type=request.getParameter("encrypt_type");String nonce=request.getParameter("nonce");String msg_signature=request.getParameter("msg_signature");Log.logger.info("timestamp:"+timestamp);Log.logger.info("encrypt_type:"+encrypt_type);Log.logger.info("nonce:"+nonce);Log.logger.info("msg_signature:"+msg_signature);//验证通过后StringBuilder sb = new StringBuilder();BufferedReader in = request.getReader();String line;while ((line = in.readLine()) != null) {sb.append(line);}String xml = sb.toString();Log.logger.info("微信推送的原生:"+xml);String encodingAesKey =WeChatContants.encodingAesKey;// 第三方平台组件加密密钥String appId=WeChatContants.THRID_APPID;//从xml中解析WXBizMsgCrypt pc = new WXBizMsgCrypt(WeChatContants.token, encodingAesKey,appId);xml = pc.decryptMsg(msg_signature, timestamp, nonce, xml);Log.logger.info("解密后的:"+xml); Map<String, String> parseXml = WeChatUtils.parseXml(xml);String msgType=parseXml.get("MsgType");String toUserName=parseXml.get("ToUserName");String fromUserName=parseXml.get("FromUserName");if("event".equals(msgType)){ Log.logger.info("---------------事件消息--------"); //排重boolean repeatFlag = messageExcludeRepeat(0, null, fromUserName,parseXml.get("CreateTime"));if(repeatFlag){//不重复的String event = parseXml.get("Event");replyEventMessage(request,response,event,toUserName,fromUserName); }else{//重复的,先回复空串,稍后调用客服接口回复WeChatUtils.responseReplyMessage(response, "");//调用客服replyApiTextMessage(request,response,null,fromUserName,0); }}else if("text".equals(msgType)){ Log.logger.info("---------------文本消息--------"); //排重boolean repeatFlag = messageExcludeRepeat(1, parseXml.get("MsgId"), fromUserName,parseXml.get("CreateTime"));if(repeatFlag){//不重复的String content = parseXml.get("Content"); processTextMessage(request,response,content,toUserName,fromUserName); }else{//重复的,先回复空串,稍后调用客服接口回复WeChatUtils.responseReplyMessage(response, "");//调用客服replyApiTextMessage(request,response,null,fromUserName,0); }} }
- 回复事件和消息类型的xml
其实最后的xml都是文本消息,格式为
sb.append("<xml>"); sb.append("<ToUserName><![CDATA["+fromUserName+"]]></ToUserName>"); sb.append("<FromUserName><![CDATA["+toUserName+"]]></FromUserName>"); sb.append("<CreateTime>"+createTime+"</CreateTime>"); sb.append("<MsgType><![CDATA[text]]></MsgType>"); sb.append("<Content><![CDATA["+content+"]]></Content>"); sb.append("</xml>");
微信对于事件回复格式的要求:xml中content格式为:文本消息的:event + “from_callback”(例如:LOCATIONfrom_callback)。
微信对于文本时要判断推送过的xml里的content的内容来回复不同的内容。
微信官方参考文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318611&token=5b3df85fa31c9a24daff2e2eac40b549d17a555e&lang=zh_CN
回复事件代码:
/*** 微信全网接入 事件消息* @param request* @param response* @param event* @param toUserName* @param fromUserName* @throws Exception */private void replyEventMessage(HttpServletRequest request,HttpServletResponse response, String event, String toUserName,String fromUserName) throws Exception {String content = event + "from_callback"; Log.logger.info("--------------事件回复消息 content="+content + " toUserName="+toUserName+" fromUserName="+fromUserName); replyTextMessage(request,response,content,toUserName,fromUserName); }
回复文本消息:
/*** 微信全网接入 文本消息* @param request* @param response* @param event* @param toUserName* @param fromUserName*/private void processTextMessage(HttpServletRequest request, HttpServletResponse response,String content,String toUserName, String fromUserName) throws Exception{ if("TESTCOMPONENT_MSG_TYPE_TEXT".equals(content)){ String returnContent = content+"_callback"; replyTextMessage(request,response,returnContent,toUserName,fromUserName); }else if(StringUtils.startsWithIgnoreCase(content, "QUERY_AUTH_CODE")){ //先回复空串WeChatUtils.responseReplyMessage(response,"");//接下来客服API再回复一次消息 replyApiTextMessage(request,response,content.split(":")[1],fromUserName,1); } }
- 回复微信服务器文本消息
公用方法:根据上述不同的content封装xml,加密发送给微信服务器。注意:响应pw.write()完后需要close()才能被微信响应成功。
/** * 回复微信服务器"文本消息" * @param request * @param response * @param content * @param toUserName * @param fromUserName * @throws DocumentException * @throws IOException */ public void replyTextMessage(HttpServletRequest request, HttpServletResponse response, String content, String toUserName, String fromUserName) throws Exception { Long createTime = Calendar.getInstance().getTimeInMillis() / 1000; StringBuffer sb = new StringBuffer(); sb.append("<xml>"); sb.append("<ToUserName><![CDATA["+fromUserName+"]]></ToUserName>"); sb.append("<FromUserName><![CDATA["+toUserName+"]]></FromUserName>"); sb.append("<CreateTime>"+createTime+"</CreateTime>"); sb.append("<MsgType><![CDATA[text]]></MsgType>"); sb.append("<Content><![CDATA["+content+"]]></Content>"); sb.append("</xml>"); String replyMsg = sb.toString(); String returnvaleue = ""; try { String encodingAesKey =WeChatContants.encodingAesKey;// 第三方平台组件加密密钥String appId=WeChatContants.THRID_APPID;//从xml中解析WXBizMsgCrypt pc = new WXBizMsgCrypt(WeChatContants.token,encodingAesKey,appId);returnvaleue = pc.encryptMsg(replyMsg, createTime.toString(),request.getParameter("nonce")); Log.logger.info("------------------加密后的返回内容 returnvaleue: "+returnvaleue); } catch (AesException e) { e.printStackTrace(); } WeChatUtils.responseReplyMessage(response, returnvaleue);}
- 客服回复用户消息
客户接口url:
https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
method:post
参数:json
{
“touser”:”OPENID”,
“msgtype”:”text”,
“text”:
{
“content”:”Hello World”
}
}
/*** 客服接口回复粉丝信息* @param response* @param auth_code 当type=1是才有值,type=0为null* @param fromUserName* @param type 1 表示全网发布时回复* 0 表示普通消息回复* @throws Exception*/public void replyApiTextMessage(HttpServletRequest request, HttpServletResponse response, String auth_code, String fromUserName,int type) throws Exception { //从数据库中获取access_tokenint companyId=Integer.parseInt(ConfigUtil.getString("resource/resource","BZ_ID"));String access_token = getThridToken(companyId);//模拟客户回复文本消息Object[]objects={access_token};String sendMessageTextUrl = MessageFormat.format(WeChatContants.THRID_KEFU_SENDMESSAGE_URL, objects);//组装post数据WeChatKeFuSendTextMessageVo textmessageVo=new WeChatKeFuSendTextMessageVo();textmessageVo.setMsgtype("text");textmessageVo.setTouser(fromUserName);WeChatKeFuSendTextVo textContentVo=new WeChatKeFuSendTextVo();String textContent = "";if(type==1){//全网发布回复的内容textContent=auth_code+"_from_api";}else{//普通文本消息回复的内容textContent="hello,ok!";}textContentVo.setContent(textContent);textmessageVo.setText(textContentVo);String result = HttpNetUtils.getInstance().httpByJson(sendMessageTextUrl,"POST",textmessageVo);Log.logger.info("客服回复结果:"+result);}
- 最后附上
/*** 统一回复微信服务器 * @param response* @param content* @throws IOException*/public static void responseReplyMessage(HttpServletResponse response,String content) throws IOException{PrintWriter pw = response.getWriter();pw.write(content);pw.flush();pw.close();}
以上内容是最近几天断断续续的整理出来的,内容可能不是一气呵成,希望大家能多多指教,共同进步,谢谢。
java微信第三方平台全网发布(三)相关推荐
- 微信开放平台-第三方平台-全网发布接入【java版本】
微信给出的文档 概述 在第三方平台方创建成功并最终开发测试完毕,提交全网发布申请时,微信服务器会通过自动化测试的方式,检测服务的基础逻辑是否可用,在确保基础可用的情况下,才会允许公众号第三方平台提交全 ...
- 微信公众账号第三方平台全网发布源码(java)- 实战测试通过
微信公众账号第三方平台全网发布源码(java)- 实战测试通过 (更多资料,关注论坛:www.jeecg.org) 技术交流请加:289709451.287090836 package org.jee ...
- 微信公众平台第三方平台全网发布 java
小弟初次写,写的不好,大神多多关照 总共分为两部分: 1.授权,微信每10分钟会给第三方平台推送一次,这里有需要用到的 COMPONENT_VERIFY_TICKET,并且需要响应 success. ...
- 微信开放平台(公众号第三方平台) -- 全网发布
一.微信开放平台,第三方平台,全网发布怎么通 过? 二. 微信开放平台 全网发布 组件ticket检测失败? 解决步骤 1.将附件中的代码发布到你配置的域名下: 2.直接点全网发布: 3. ...
- 微信开放平台全网发布
最近刚做了微信开放平台全网发布的开发,整理一下贴出来 前置条件 已经做好了相关的开发工作(比如扫码授权之类的),项目导入了微信SDK(最后会附上我自己用的SDK jar包) sdk jar 地址: h ...
- 微信三方平台全网发布总结
相信很多第一次玩三方平台全网发布的童鞋同会遇到很多问题.这里将这两天我们在全网发布测试中遇到的问题做个总结,希望对大家有用: 在这里首先感慨一下微信有点店大欺客的感觉,文档写的确实不咋地,包括微信支付 ...
- mysql 推送微信公众号_10分钟完成微信公众号第三方平台全网发布
背景:在微信公众平台配置服务器URL时,使用了新浪云SAE自带的二级域名,提交时出现一个安全风险的警告,网上查了下,许多服务平台和团队也遇到同样的问题. 经过一番研究 - 为什么会有安全风险的警告? ...
- 微信开放平台全网发布时,检测失败 —— C#
主要就是三个:返回API文本消息,返回普通文本消息,发送事件消息 --会出现失败的情况 (后续补充说明:出现检测出错,不一定是代码出现了问题,也有可能是1.微信方面检测时出现服务器请求失败,2.我 ...
- 开放平台全网发布php,微信开放平台 全网发布 组件ticket检测失败
api接口文本错误的 之前我也是遇到问题 困扰几天了终于搞好了 问题是这样解决的 发现有点坑爹 他这两个接口要求的code取值不一样 第三方平台方拿到$query_auth_code$的值后,通过接口 ...
最新文章
- Python链接MySQL
- ios可变数组的操作
- 上传文本到hdfs上的一些命令
- CodeForces - 1523D Love-Hate(随机数+状压dp)
- JDK源码解析之 Java.lang.Short
- 熟悉c语言运行环境实验原理,c语言实验报告1
- HDU 2874 Connections between cities(LCA离线算法实现)
- Installing specific major Java JDK versions on OS X via Homebrew
- astr在python_python学习之初识字符串
- 为什么很多人转行学习Web前端技术?
- eclipse导入外部项目引发的错误以及解决方案
- 【React教学】通用型DataTable组件——400行内
- netfilter/iptables
- hdu 4311 Meeting point-1 递推 多校联合赛(二) 第二题
- 类数组变量定义与初始化
- Python(含PyCharm及配置)下载安装以及简单使用(Idea)
- 搜索引擎网站登录入口
- httpclient 下载大文件
- Linux环境下向github上传代码(生成token、生成本地密钥)
- ca盘显示无证书_CA证书使用问题及解决方法