前言
最近试水了一下微信公众号,花了一天开发,记录下其中的坑,分享给大家。附上公众号二维码,实现讲笑话的功能,欢迎大家关注。
该公众号实现功能:
1、发送任意内容,随机返回笑话
2、笑话调用了网上开发接口
3、由于接口每日查询次数限制,所以查询后的笑话会保存在数据库中,便于后续使用
微信公众号注册
注册公众号微信官方链接,注意配置一下 URL,Token。
- URL必须以http://或https://开头,分别支持80端口和443端口。
- Token 必须为英文或数字,长度为3-32字符。
服务端使用的是 Java,感觉后台使用 PHP、Python 的多一些,把 Java 开发遇到的问题记录一下,方便自己也方便别人查阅。
微信第一次接入 验证有效性
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
/** 判断是否是微信接入激活验证,只有首次接入验证时才会收到echostr参数,此时需要把它直接返回 */
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
if (WechatSignUtil.checkSignature(signature, timestamp, nonce)) {
// 验证 有效性
out.write(echostr);
out.flush();
out.close();
}
}
刚才微信公共平台配置的 token
这儿填写,保持一致。
public class WechatSignUtil2 {
// 与接口配置信息中的 Token 要一致
private static String token = "***";
/**
* 验证签名
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 将 token、timestamp、nonce 三个参数进行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行 sha1 加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将 sha1 加密后的字符串可与 signature 对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
}
微信事件处理
微信的事件包括以下几种,本文实现了最基本的关注后,回复一段简单的使用说明。
事件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(取消订阅)
1 . 关注/取消关注事件
2 . 扫描带参数二维码事件
3 . 上报地理位置事件
4 . 自定义菜单事件
5 . 点击菜单拉取消息时的事件推送
6 . 点击菜单跳转链接时的事件推送
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resp) {
try {
Map<String, String> map = WechatMessageUtil.parseXml(request);
String msgtype = map.get("MsgType");
String result = "success";
if (WechatMessageUtil.REQ_MESSAGE_TYPE_EVENT.equals(msgtype)) {
result = WechatEventDispatcherHappy.processEvent(map); // 进入事件处理
} else {
result = WechatMsgDispatcherHappy.processMessage(map); // 进入消息处理
}
PrintWriter out = resp.getWriter();
out.print(result);
} catch (Exception e) {
}
}
下面这个类处理了相关事件
public class WechatEventDispatcherHappy {
public static String WechatValue = "success";
public static String processEvent(Map<String, String> map) {
if (map.get("Event").equals(WechatMessageUtil.EVENT_TYPE_SUBSCRIBE)) { // 订阅
String result = "你关注我了哈~\n\n发送任意内容获取笑话,天天开心哦~";
String openid = map.get("FromUserName"); // 用户 openid
String mpid = map.get("ToUserName"); // 公众号原始 ID
// 普通文本消息 回复的消息
WechatRespTextMessage txtmsg = new WechatRespTextMessage();
txtmsg.setToUserName(openid);
txtmsg.setFromUserName(mpid);
txtmsg.setCreateTime(new Date().getTime());
txtmsg.setMsgType(WechatMessageUtil.RESP_MESSAGE_TYPE_TEXT);
txtmsg.setContent(result);
return WechatMessageUtil.textMessageToXml(txtmsg);
}
return WechatValue;
}
}
微信消息处理
消息分为 文本消息、语音消息、图片消息、视频消息、小视频消息等,本文实现发送任意消息,都给回复一条随机笑话。
public class WechatMsgDispatcherHappy {
static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
public static String processMessage(Map<String, String> map) {
if (map.get("MsgType").equals(WechatMessageUtil.REQ_MESSAGE_TYPE_TEXT)) { // 文本消息
// System.out.println("==============这是文本消息!");
String openid = map.get("FromUserName"); // 用户 openid
String mpid = map.get("ToUserName"); // 公众号原始 ID
String createTime = map.get("CreateTime"); // 公众号原始 ID
String myDate = formatter.format(new Date(Long.parseLong(createTime) * 1000L));
String content = map.get("Content").trim(); // 文本内容
// 普通文本消息 回复的消息
WechatRespTextMessage txtmsg = new WechatRespTextMessage();
txtmsg.setToUserName(openid);
txtmsg.setFromUserName(mpid);
txtmsg.setCreateTime(new Date().getTime());
txtmsg.setMsgType(WechatMessageUtil.RESP_MESSAGE_TYPE_TEXT);
// 随机数
Random random = new Random();
// 获取随机笑话
InfoHash result = JuheUtil.getRequest1(random.nextInt(400000) + "");
txtmsg.setContent(result.getRes());
return WechatMessageUtil.textMessageToXml(txtmsg);
}
if (map.get("MsgType").equals(WechatMessageUtil.REQ_MESSAGE_TYPE_IMAGE)) { // 图片消息
// System.out.println("==============这是图片消息!");
}
return "success"; // 默认回复 success,立刻回复消息
}
}
数据接口
本来想找自己写个笑话随机接口,后来搜了一下,网上有现成的接口,而且还是免费的。
可以通过我的邀请码
https://www.juhe.cn/register?tjc=B71E9316F62D8E95 注册该接口。
接口地址:http://japi.juhe.cn/joke/content/list.from
支持格式:json
请求方式:http get
请求示例:http://japi.juhe.cn/joke/content/list.from?key=您申请的KEY&page=2&pagesize=10&sort=asc&time=1418745237
接口备注:根据时间戳返回该时间点前或后的笑话列表
JSON返回示例:
{
"error_code": 0,
"reason": "Success",
"result": {
"data": [
{
"content": "某日我坐高铁从杭州去上海,半路停下来了几十分钟一直没有开动,这个时候列车员来到我们车厢解释原因。 有一乘客见状故意询问,列车为何还不开车。 列车解释列车出现故障。乘客见状很是担心说了一句,你们再不开,后面的车追上来怎么办?车厢乘客无一不笑!!",
"hashId": "d4698e565a18d099be731ba53d18d8e4",
"unixtime": 1486254830,
"updatetime": "2017-02-05 08:33:50"
}
]
}
}
JSON 数据解析
使用 GSON 对 JSON 数据解析,注意该 JSON 比较复杂,result
是一个对象,其中data
是一个数组。
Gson gson = new Gson();
HappyGetResult happy = gson.fromJson(result, HappyGetResult.class);
List<HappyData> data = happy.getResult().getData();
String content = d.get(0).getContent();
String hash = d.get(0).getHashId();
其中
public class HappyGetResult {
@SerializedName("error_code")
public int errorCode;
@SerializedName("reason")
public String reason;
@SerializedName("result")
public HappyInfo result;
}
public class HappyInfo {
List<HappyData> data;
public class HappyData {
String content;
String hashId;
String unixtime;
String updatetime;
}