这篇讲解对微信公众号开发DEMO分析,主要的目的是了解它的原理和微信API的使用。
然后进行修改,实现我们想要的功能,如:菜单的创建、信息交互、跳转到自定义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] 纯文本查看 复制代码
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 CallbackService {
// 自定义菜单中的key值
public static final String V1001_LIGHT_ON = "V1001_LIGHT_ON";//点灯
public static final 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] 纯文本查看 复制代码 // 文本消息
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] 纯文本查看 复制代码
// 文本消息
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] 纯文本查看 复制代码
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="com.bluelight.tools.Tools"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
调用类函数的方法:
<%
Tools ts= new Tools();
ts.createMenu();
%>
</body>
</html>
这时再修改一个这个文件的代码如上图,这样就可以成功调用类文件了,但不建议这么调用不正规,只是方便而以。
再运行,输出如下信息,说明成功!
九、打包成War文件。
开始导出war文件。
导出一个文件名,保存就能成功导出war文件,之后就可以上传到服务器了。
十、总结。
原来的DEMO不支持https请求,也没有jsp文件,这些都是要自己建立的。
经过对微信Demo的源代码分析、修改、调式,那么我们这一课节就完成了。
附源代码下载:http://pan.baidu.com/s/1skSC9jf
请关注@智能创客 微信:znck007(打造DIY创客平台)
|