然后进行修改,实现我们想要的功能,如:菜单的创建、信息交互、跳转到自定义HTML5页面等等。
一、目录结构讲解。
展开目录后,按照1、2、3、4、5的顺序讲解:
1、web.xml网站配置(上面图片标错了,是在WEB-INF未展来的目录里的web.xml)
2、公众账号信息配置。
3、微信推送消息的入口。
4、处理微信消息的业务类。
5、相关微信API的使用方法,创建菜单,进行设备授权,生成二维码等。
二、web.xml(程序引导配置)
双击web.xml,就会看到网站配置的内容
1、声明地址名称,绑定相关的Servlet类,以后访问就用这个地址。
2、将名称映射到地址。
这样就能成功访问:http://你的域名/callback来访问com.bluelight.demo.web.CallbackServlet类了。
我们运行,右击bluelight->Run As->Run Server,然后Finish。
1、在后面添加callback,回车。
2、出现Null异常,说明已经成功调用了类,只是没有传参数。
那我们如何添加一个访问类地址呢?
我们复制,访着实现一个能使用calltest访问的地址。
重新运行,然后访问http://localhost:8080/demo/calltest,成功调用了类文件。
这里还有一种是jsp,可以直接放到WebContent目录里,直接访问文件名就可以,不需要做映射。
三、config.properties(微信公众号appID、appsecret、taken设置)
双击config.properties,打开后appID、appsecret、taken都是空的,我们要复制进去。
登陆公众号开发测试网址:
http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
直接复制appID、appsecret填写,URL是我们之后要教大家申请的服务器地址,taken自定义一个就可以了。
四、CallbackServlet.java(入口类讲解)
打开类,主要看到三个类:doGet、doPost、out函数。
在这里的doGet函数,基本不做事,只接收到传来的值,然后直接就返回空的值回去了,如果验证不通过就返回错误而以。
doPost涵数,多了解决xml,然后使用CallbackService业务类进行处理(这才是关键的地方),然后调用out函数才返回给微信。
out函数,使用了Writer类,然后输出返回,这样微信就能收到啦。
五、CallbackService.java(微信消息业务类讲解)
在入口类的doPost涵数里,调用了这个业务类,那我们来一一分析它。
这个类就只有一个事件处理函数handle
[Java] 纯文本查看 复制代码
001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 | package com.bluelight.demo.service; import java.util.Map; import org.apache.commons.codec.binary.Base64; import com.bluelight.demo.api.DeviceApi; import com.bluelight.demo.api.MpApi; import com.bluelight.demo.consts.MsgType; import com.bluelight.demo.consts.XmlResp; import com.bluelight.demo.mock.DBMock; import com.bluelight.demo.protocol.BlueLight; import com.bluelight.demo.protocol.BlueLight.CmdId; /** * 回调业务处理 */ public class // 自定义菜单中的key值 public static String V1001_LIGHT_ON = "V1001_LIGHT_ON" ; //点灯 public static String V1002_LIGHT_OFF = "V1002_LIGHT_OFF" ; //灭灯 public String handle(Map<String, String> reqMap) throws Exception { String msgType = reqMap.get( "MsgType" ); String fromUser = reqMap.get( "FromUserName" ); String toUser = reqMap.get( "ToUserName" ); // 针对不同类型的消息和事件进行处理 // 文本消息 if (MsgType.TEXT.equals(msgType)) { // 可以在此处进行关键字自动回复 String content = "收到文本消息:" + reqMap.get( "Content" ); return XmlResp.buildText(fromUser, toUser, content); } // 基础事件推送 if (MsgType.EVENT.equals(msgType)) { String event = reqMap.get( "Event" ); // 关注公众号 if (MsgType.Event.SUBSCRIBE.equals(event)) { // 回复欢迎语 return XmlResp.buildText(fromUser, toUser, "欢迎关注蓝牙灯泡demo测试公众号!" ); } // 菜单点击事件 if (MsgType.Event.CLICK.equals(event)) { // 根据key值判断点击的哪个菜单 String eventKey = reqMap.get( "EventKey" ); // 点灯/灭灯 if (V1001_LIGHT_ON.equals(eventKey) || V1002_LIGHT_OFF.equals(eventKey)) { //是否点灯操作 boolean open = V1001_LIGHT_ON.equals(eventKey); // 根据 fromUserName 获取绑定的信息 Map<String, String> boundInfo = DBMock.queryBoundInfo(fromUser); // 未绑定 if (boundInfo == null ) { return XmlResp.buildText(fromUser, toUser, "未绑定" ); } String deviceType = boundInfo.get( "deviceType" ); String deviceID = boundInfo.get( "deviceID" ); String openID = boundInfo.get( "openID" ); // 构造设备消息 CmdId cmdId = open ? BlueLight.CmdId.OPEN_LIGHT_PUSH : BlueLight.CmdId.CLOSE_LIGHT_PUSH; byte [] respRaw = BlueLight.build(cmdId, null , ( short ) 0 ).toBytes(); // Base64编码 final String content = Base64.encodeBase64String(respRaw); // 推送消息给设备 DeviceApi.transMsg(deviceType, deviceID, openID, content); // 回复 boolean debug = true ; if (debug){ // 返回调试信息 String debugText = "已发送" + (open ? "点灯" : "灭灯" ) + "消息:" + "deviceID为" + deviceID + ",设备消息为" + content; return XmlResp.buildText(fromUser, toUser, debugText); } else { return "" ; } } } } // 设备消息或事件 if (MsgType.DEVICE_EVENT.equals(msgType) || MsgType.DEVICE_TEXT.equals(msgType)) { String reqContent = reqMap.get( "Content" ); String deviceType = reqMap.get( "DeviceType" ); String deviceID = reqMap.get( "DeviceID" ); String sessionID = reqMap.get( "SessionID" ); final String openID = reqMap.get( "OpenID" ); // 设备事件推送 if (MsgType.DEVICE_EVENT.equals(msgType)) { String event = reqMap.get( "Event" ); // 绑定/解绑事件 if (MsgType.DeviceEvent.BIND.equals(event) || MsgType.DeviceEvent.UNBIND.equals(event)) { // 存储用户和设备的绑定关系 if (MsgType.DeviceEvent.BIND.equals(event)){ DBMock.saveBoundInfo(reqMap); } else { DBMock.removeBoundInfo(reqMap.get( "FromUserName" )); } // 设备绑定/解绑事件可以回复空包体 return "" ; } } // 收到设备消息 if (MsgType.DEVICE_TEXT.equals(msgType)) { // Base64解码 byte [] reqRaw = Base64.decodeBase64(reqContent); // 反序列化 BlueLight lightReq = BlueLight.parse(reqRaw); // 逻辑处理 // demo中 推送消息给用户微信 String reqText = lightReq.body; System.out.println( "recv text:" + reqText); String transText = "收到设备发送的数据:" ; byte [] reqTextRaw = reqText.getBytes( "UTF-8" ); if (reqTextRaw.length > 0 && reqTextRaw[reqTextRaw.length - 1 ] == 0 ) { // 推送给微信用户的内容去掉末尾的反斜杠零'\0' transText = transText + new String(reqTextRaw, 0 , reqTextRaw.length - 1 , "UTF-8" ); } else { transText = transText + reqText; } // 推送文本消息给微信 MpApi.customSendText(openID, transText); // demo中 回复 收到的内容给设备 BlueLight lightResp = BlueLight.build(BlueLight.CmdId.SEND_TEXT_RESP, reqText, lightReq.head.seq); // 序列化 byte [] respRaw = lightResp.toBytes(); // Base64编码 String respCon = Base64.encodeBase64String(respRaw); // 设备消息接口必须回复符合协议的xml return XmlResp.buildDeviceText(toUser, fromUser, deviceType, deviceID, respCon, sessionID); } } // 未处理的情况返回空字符串 return "" ; } } |
代码主要针对不同类型的消息,进行处理,如TEXT,EVENT,DEVICE_EVENT,DEVICE_TEXT。
TEXT是纯文本发过来,含表情。
EVENT是关注公众号,取消公众号,菜单点击等等,
DEVICE_EVENT是硬件的消息,DEVICE_TEXT就是硬件发送过来的文本(一般用文本交互就可以了)
在菜单点击事件里,实现了开和关,这里获取硬件的信息,前提是先扫二维码绑定,这个之后讲硬件时会讲到的。
在设备事件里,如果是绑定事件和解绑事件,就进行临时的保存和删除。
这里处理收到设备的信息处理。
[Java] 纯文本查看 复制代码
123456789 | // if (MsgType.TEXT.equals(msgType)) { // 可以在此处进行关键字自动回复 String content = "收到文本消息:" + reqMap.get( "Content" ); if (reqMap.get( "Content" ).equals( "你好" )){ content = "你也好,你全家都好。" ; } return XmlResp.buildText(fromUser, toUser, content); } |
这里我们做一下修改,添加一段代码,如果用户发送“你好”两字,那么我直接回复他:你也好,你全家都好。
if(content.equals("你好")){
content = "你也好,你全家都好。";
}
六、Tools.java(操作微信API类讲解)
Tools.java共有main、createQrByDeviceId、createQrImage、device_Auth、createMenu等5个函数。
分别为主函数、建立二维码、生成图片、授权设备、建立菜单。
这里我们只跳用建立菜单,二维码和授权,之后在说硬件的时候再教大家。
[Java] 纯文本查看 复制代码
010203040506070809101112 | // if (MsgType.TEXT.equals(msgType)) { // 可以在此处进行关键字自动回复 String content = "收到文本消息:" + reqMap.get( "Content" ); if (reqMap.get( "Content" ).equals( "你好" )){ content = "你也好,你全家都好。" ; } if (reqMap.get( "Content" ).equals( "菜单" ) ){ Tools.createMenu(); //建立菜单 } return XmlResp.buildText(fromUser, toUser, content); } |
跟上篇一样,用户发送菜单二字,我们就调用并生成菜单
提醒:(菜单生成微信要返回主界面,再进一次微信,同时设置第二次有时要24小时内才能生效)
但这里完成,经过小编测试,也还是不成功,原因是因为当前微信的demo不支持https请求(也不知道为什么微信demo自己不调试好),那我们再改改按下一个步聚来做就可以了。
七、支持Https的POST(默认DEMO不支持,修改代码)
我们要在这里添加二个文件,原本是没有的。
添加进去,SSLClient.java、MyX509TrustManager.java这两个文件就是实现https的请求的(修改后的DEMO在最后面提供给大家下载的)
我们还需要在http请求的地方调用SSLClient类,如上图,我们在executeGet、executePost添加了一段代码。
if( url.indexOf("https")>=0 ){
httpclient =new SSLClient();
}else{
httpclient = new DefaultHttpClient();
}
如果是https那么就自动请求ssl,实现可以请求https了。
八、JSP文件调用类文件。
右击WebContent文件-》New->JSP File,新建一个jsp文件,取名为test.jsp,然后点击finish。
自动生成jsp文件,然后运行。
网址后面加test.jsp,然后回车,就能看到白白的页面,访问成功。
[Java] 纯文本查看 复制代码
01020304050607080910111213141516 | <%@ "java" contentType= "text/html; charset=UTF-8" pageEncoding= "UTF-8" import = "com.bluelight.tools.Tools" %> <!DOCTYPE "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html> <head> <meta "Content-Type" content= "text/html; charset=UTF-8" > <title>Insert </head> <body> 调用类函数的方法: <% Tools ts= new Tools(); ts.createMenu(); %> </body> </html> |
这时再修改一个这个文件的代码如上图,这样就可以成功调用类文件了,但不建议这么调用不正规,只是方便而以。
再运行,输出如下信息,说明成功!
九、打包成War文件。
开始导出war文件。
导出一个文件名,保存就能成功导出war文件,之后就可以上传到服务器了。
十、总结。
原来的DEMO不支持https请求,也没有jsp文件,这些都是要自己建立的。
经过对微信Demo的源代码分析、修改、调式,那么我们这一课节就完成了。
附源代码下载: http://download.csdn.net/detail/hunhun1122/9807706