微信作为移动互联网的第一张门票,是我们理解和连接世界的一条很好的路径,是我们在移动互联网时代的ID。而微信公众号可以建立起一对多的较强的服务与用户之间的连接,公众号分为订阅号、服务号、企业号三种,提供不同的侧重点:订阅号每天推送,服务号一个月能推送一次。我在业余时间开发了一个微信公众号JavaDeveloper,实现的功能包括历史上的今天、发送来QQ表情返回原来的QQ表情、搜索歌曲、翻译、人脸识别、机器人聊天、天气预报(机器人自己实现了)、周边搜索。。。。本微信号只是我的学习之作,没有进行系统的设计,微信公众号的开发首先需要定位,还要找到好的宣传渠道。
本文总结我的微信开发历程,主要包括微信的开发步骤、调用第三方的Webservice、设计模式的运用,是一个挺综合的项目,与其说是微信公众号开发,倒不如说JavaWeb开发。
微信公众号可以提供给关注这个公众号的用户的功能完全由这个公众号的后台服务器完成。公众号与用户之间发送的消息和公众号与服务器之间的通信消息都是XML格式,可以看做微信的服务器调用公众号的服务器的Webservice,后者作为前者的客户端。
由于通信格式XML的平台独立化,公众号的服务器可以使普通的Web服务器,不限编程语言、平台等一切Webservice的优点都具备。微信好像推荐使用PHP,其例程都是PHP的,但是由于我对Java比较熟悉,所以选择了Java作为开发语言。实际上微信公众号的开发就是普通的Web开发,别无二至。我们既可以使用最基础的Servlet,也可以使用SSH等一切Web开发的技术,这里我选择最简单的Servlet。
微信公众号的开发首先必须提供一个公网上可以访问的地址,以作为通信校验和微信服务器与你的服务器之间的通信。通信校验使用GET方式,发送给你的字符串你可以校验请求来自微信服务器;你必须返回其规定的字符串以校验握手。
非常详细的步骤我就不贴出了,详见柳峰博客、高洛峰微信开发视频教程。
有一个测试地址,输入你的公网服务器地址,发送你的消息就能获取到你的服务器地址的消息,就想在手机上玩微信一样,还能看见具体的XML信息,这个网址应该知道微信的服务器地址和发送的协议,模拟手机的微信给微信服务器发送信息,得到之后通过Ajax刷新。http://open.binguo.me/Tester,其实你自己写一个都行。只要知道 了与微信服务器之间的通信协议。
微信公众号有个至关重要的地方,别人怎么会加你关注你,一定是你的内容信息是别人需要的,随手就能加你。你的微信公众号随手就能加你二维码出现在很多地方。个人微信ID做成二维码,别人扫描后直接加好友。
/**
*
* 确认请求来自微信服务器
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//每次请求都会带上这几个除了echostr的字符串,所以在post的时候也可以利用他们验证请求来自微信
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
PrintWriter out = response.getWriter();
// 通过检验signature 对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (Signature.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
}else {
out.print("你不是微信服务器,请自重!!");
}
out.close();
out = null;
}
/**
* 请求校验工具类
*
* @author 熊诗言
* @date -09-05
*/
public class Signature {
// 与接口配置信息中的Token 要一致
private static String token = "xiongshiyan";
/**
* 验证签名
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
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 = byte2Str(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1 加密后的字符串可与signature 对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byte2Str(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byte2HexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byte2HexStr(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;
}
}
微信服务器的每次请求都是POST方式。发送来XML的数据,你也返回XML的数据。
/**
*
* 处理微信服务器的请求
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO 消息的接收、处理、响应
//将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/xml;charset=utf-8");
// 调用核心业务类接收消息、处理消息
String respMessage = WeiXinService.processRequest3(request);
// 响应消息
PrintWriter out = response.getWriter();
out.print(respMessage);
out.close();
}