微信公众平台开发学习笔记·一
2014-08-04 21:26
190 查看
不觉间毕业参加工作将近1年了。本着学点新东西和赚点外快的念头,自学了一下微信公众平台开发。在这篇文章里,主要描述了一下我搭建一个微信公众平台的"helloworld"级程序的历程。其中,柳峰老师的视频和文章给了我很大的帮助。非常感谢。
搭建微信公众平台程序其中的准备如下:
1、微信公众平台上申请微信公众号(需要手持身份证的相片,待会申请SAE也会用到)。此审批流程很快,大概1个工作日就可完成。
2、SAE云服务器(初次注册赠送500云豆,可使用100小时左右,用刚才申请微信公众号的相片可以申请实名验证,通过后会再给2000云豆)
3、开发工具。此处我用的是eclipse4.4 jdk1.6(据说SAE只支持1.6,此处未考证) dom4j1.6.1 xstream1.3
接下来就是环境的搭建。eclipse和jdk均解压缩即可使用,注意的是eclipse和jdk的系统位数要一致,都为32位或64位,视你的系统而定。打开eclipse后新建一个web工程。在工程中新建一个servlet类,其中doGet方法即为用户在访问你的公众号时微信服务器向你发送的验证请求。当验证通过后,微信才会将接下来的消息发送post请求到你的服务上。简单的说,就是微信起到一个中转站的作用,用户的请求经过微信转发到我们提供给微信的服务上(这里是SAE),然后由我们部署到服务上的程序做处理后再把响应消息回复给微信,由微信转发给用户。doGet方法在这里做的就是验证请求是否能够送达我们提供给微信的服务。
微信调用doGet方法时会带有4个参数signature,timestamp,nonce,echostr 这时我们还要用到一个参数就是token
图中是你在微信公众号申请下来后,点击开发者模式弹出来的东西。这里的URL就是你提供给微信的服务地址,微信会将用户的请求转发到这个服务地址上。而画红的Token可以理解为一个个人签名,我在这里用的是LogicCode。等下这个会在程序中用到。接着说doGet的参数,在这里我们要将timestamp.nonce,token这3个参数进行字典排序,并进行sha-1加密后得出来的字符串与signature进行比较,如果相等,则将echostr返回给微信,说明验证已经通过了。代码和包体结构如下:
其中采用SHA-1加密也可以写成
还值得一提的就是Arrays.sort这个方法会根据元素的类型和个数来采用不同的排序方法。当类型为基本类型,且个数小于7的时候采用的是冒泡排序,大于等于7的时候用的是快速排序。当类型为object时,采用归并排序,当归并的数组长度小于7的时候采用冒泡排序。
而toHexString方法中的参数说明,在网上查阅如下:
如果是一个byte(8位)类型的数字,他的高24位里面都是随机数字,低8位才是实际的数据。java.lang.Integer.toHexString() 方法的参数是int(32位)类型,如果输入一个byte(8位)类型的数字,这个方法会把这个数字的高24为也看作有效位,这就必然导致错误,使用& 0XFF操作,可以把高24位置0以避免这样错误的发生。
至此,我们的doGet方法就写完了,将工程右键export出一个war包就可以部署到SAE上了。这时SAE会给我们一个链接,这个链接就是填在图1中的url上的。我们可以访问一下该地址。如果出现下图,就证明你部署成功,且微信能够访问到你的服务了。
500是你的请求没有传参数,不用担心。
接下来就是写doPost方法用以响应用户的请求。用户的请求会由微信通过post请求到我们,这里只写了一个简单的文本信息回应和关注事件的回应。
MainService
这里主要用了dom4j将微信发给我们的XML解析成节点字符串,然后再用xstream将我们返回给微信的字符串组装成XML,其中扩展了xstream让其增加CDATA标记。
再将这个工程导出成war包部署到SAE上,我们在用自己的微信搜索我们的公众号关注时便会有事件响应,发送我是谁时,也会有事件响应了。
如果没有响应,请检查一下打出的war包里是否包含有dom4j和xstream的jar包,如果没有的话,右键web工程选择build path中的deployment assembly将两个jar包添加进去即可。
第一次写这种类型的博客,如有错误还请大家多多见谅。
搭建微信公众平台程序其中的准备如下:
1、微信公众平台上申请微信公众号(需要手持身份证的相片,待会申请SAE也会用到)。此审批流程很快,大概1个工作日就可完成。
2、SAE云服务器(初次注册赠送500云豆,可使用100小时左右,用刚才申请微信公众号的相片可以申请实名验证,通过后会再给2000云豆)
3、开发工具。此处我用的是eclipse4.4 jdk1.6(据说SAE只支持1.6,此处未考证) dom4j1.6.1 xstream1.3
接下来就是环境的搭建。eclipse和jdk均解压缩即可使用,注意的是eclipse和jdk的系统位数要一致,都为32位或64位,视你的系统而定。打开eclipse后新建一个web工程。在工程中新建一个servlet类,其中doGet方法即为用户在访问你的公众号时微信服务器向你发送的验证请求。当验证通过后,微信才会将接下来的消息发送post请求到你的服务上。简单的说,就是微信起到一个中转站的作用,用户的请求经过微信转发到我们提供给微信的服务上(这里是SAE),然后由我们部署到服务上的程序做处理后再把响应消息回复给微信,由微信转发给用户。doGet方法在这里做的就是验证请求是否能够送达我们提供给微信的服务。
微信调用doGet方法时会带有4个参数signature,timestamp,nonce,echostr 这时我们还要用到一个参数就是token
图中是你在微信公众号申请下来后,点击开发者模式弹出来的东西。这里的URL就是你提供给微信的服务地址,微信会将用户的请求转发到这个服务地址上。而画红的Token可以理解为一个个人签名,我在这里用的是LogicCode。等下这个会在程序中用到。接着说doGet的参数,在这里我们要将timestamp.nonce,token这3个参数进行字典排序,并进行sha-1加密后得出来的字符串与signature进行比较,如果相等,则将echostr返回给微信,说明验证已经通过了。代码和包体结构如下:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //微信加密签名 String signature = req.getParameter("signature"); //时间戳 String timestamp = req.getParameter("timestamp"); //随机数 String nonce = req.getParameter("nonce"); //随机字符串 String echostr = req.getParameter("echostr"); 4000 PrintWriter out = resp.getWriter(); if(StringUtil.validateSignature(signature, timestamp, nonce)){ out.print(echostr); } out.close(); out = null; }
public class StringUtil { private static String token = "LogicCode"; public static boolean validateSignature(String signature,String timestamp,String nonce){ boolean result = false; String[] strArray = new String[]{token,timestamp,nonce}; //采用源码的归并排序,如果数组元素个数小于7采用冒泡排序 Arrays.sort(strArray); StringBuffer strBuffer = new StringBuffer(); for(int i = 0; i<strArray.length;i++){ strBuffer.append(strArray[i]); } MessageDigest md = null; String mdStr = null; try { //采用SHA-1加密 md = MessageDigest.getInstance("sha-1"); md.update(strBuffer.toString().getBytes()); byte[] mdResult = md.digest(); mdStr = convertHexString(mdResult); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(null!=mdStr&&mdStr.equals(signature)){ result = true; } return result; } //将字节转换成十六进制字符串 public static String convertHexString(byte[] mdResult){ StringBuffer strBuffer = new StringBuffer(); for(int i = 0;i<mdResult.length;i++){ strBuffer.append(Integer.toHexString(0xff&mdResult[i])); } return strBuffer.toString(); } }
<span style="font-family: Arial, Helvetica, sans-serif;">这里可以加强一下stringbuilder和stringbuffer区别的记忆,stringbuffer是线程安全的,因为是线程安全的所以在性能上要比stringbuilder差一些。</span></span>
其中采用SHA-1加密也可以写成
md = MessageDigest.getInstance("sha-1"); byte[] mdResult = md.digest(strBuffer.toString().getBytes());
还值得一提的就是Arrays.sort这个方法会根据元素的类型和个数来采用不同的排序方法。当类型为基本类型,且个数小于7的时候采用的是冒泡排序,大于等于7的时候用的是快速排序。当类型为object时,采用归并排序,当归并的数组长度小于7的时候采用冒泡排序。
而toHexString方法中的参数说明,在网上查阅如下:
如果是一个byte(8位)类型的数字,他的高24位里面都是随机数字,低8位才是实际的数据。java.lang.Integer.toHexString() 方法的参数是int(32位)类型,如果输入一个byte(8位)类型的数字,这个方法会把这个数字的高24为也看作有效位,这就必然导致错误,使用& 0XFF操作,可以把高24位置0以避免这样错误的发生。
至此,我们的doGet方法就写完了,将工程右键export出一个war包就可以部署到SAE上了。这时SAE会给我们一个链接,这个链接就是填在图1中的url上的。我们可以访问一下该地址。如果出现下图,就证明你部署成功,且微信能够访问到你的服务了。
500是你的请求没有传参数,不用担心。
接下来就是写doPost方法用以响应用户的请求。用户的请求会由微信通过post请求到我们,这里只写了一个简单的文本信息回应和关注事件的回应。
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); String responseMessage = ""; try { responseMessage = MainService.doRequest(req); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } PrintWriter out = resp.getWriter(); out.print(responseMessage); out.close(); out = null; }
MainService
public static String doRequest(HttpServletRequest request) throws Exception { String result = "请求未得到响应,请稍后再试";//默认返回的信息 Map<String,String> resultMap = MessageUtil.parseXML(request);//读取用户请求 String fromUserName = resultMap.get("FromUserName"); String toUserName = resultMap.get("ToUserName"); String msgType = resultMap.get("MsgType"); String content = resultMap.get("Content"); ResponseTextMessage responseTextMessage = new ResponseTextMessage();//组装返回信息 responseTextMessage.setCreateTime(new Date().getTime()); responseTextMessage.setFromUserName(toUserName); responseTextMessage.setToUserName(fromUserName); responseTextMessage.setMsgType(MessageUtil.RESPONSE_MESSAGE_TYPE_TEXT); if(msgType.equals(MessageUtil.REQUEST_MESSAGE_TYPE_EVENT)){ String eventType = resultMap.get("Event"); if(eventType.equals("subscribe")){ responseTextMessage.setContent("感谢您的关注"); }else if(eventType.equals("unsubscribe")){ } } else if(msgType.equals(MessageUtil.REQUEST_MESSAGE_TYPE_TEXT)){ if(content.equals("我是谁")){ responseTextMessage.setContent(fromUserName); } } result = MessageUtil.textMessageToXML(responseTextMessage);//将返回信息转换成XML return result; }
>public class MessageUtil { public static final String REQUEST_MESSAGE_TYPE_TEXT = "text";//请求消息为文本类型 public static final String RESPONSE_MESSAGE_TYPE_TEXT = "text";//响应消息为文本类型 public static final String REQUEST_MESSAGE_TYPE_EVENT = "event";//请求类型为事件 public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";//订阅 public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";//取消订阅 /**解析用户向公众号发送的请求消息 * @param request * @return * @throws Exception */ public static Map<String,String> parseXML(HttpServletRequest request) throws Exception{ Map<String,String> map = new HashMap<String,String>(); InputStream inputStream = request.getInputStream();//从request中取得输入流 SAXReader saxReader = new SAXReader(); Document document = saxReader.read(inputStream);//读取输入流 Element root = document.getRootElement();//获取根元素 List<Element> elementList = root.elements();//得到根元素的子节点 for(Element e:elementList){//遍历节点 map.put(e.getName(), e.getText()); } inputStream.close(); inputStream = null; return map; } /**将响应的文本消息转换成XML * @param responseTextMessage * @return */ public static String textMessageToXML(ResponseTextMessage responseTextMessage){ xstream.alias("xml", responseTextMessage.getClass()); return xstream.toXML(responseTextMessage); } /** * 扩展xstream,使其支持CDATA块 * * @date 2013-05-19 */ private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 对所有xml节点的转换都增加CDATA标记 boolean cdata = true; @SuppressWarnings("unchecked") public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } });
这里主要用了dom4j将微信发给我们的XML解析成节点字符串,然后再用xstream将我们返回给微信的字符串组装成XML,其中扩展了xstream让其增加CDATA标记。
再将这个工程导出成war包部署到SAE上,我们在用自己的微信搜索我们的公众号关注时便会有事件响应,发送我是谁时,也会有事件响应了。
如果没有响应,请检查一下打出的war包里是否包含有dom4j和xstream的jar包,如果没有的话,右键web工程选择build path中的deployment assembly将两个jar包添加进去即可。
第一次写这种类型的博客,如有错误还请大家多多见谅。
相关文章推荐
- 微信公众平台开发学习笔记1--验证服务器地址的有效性
- 微信公众平台开发学习笔记2--获取access token
- 开发asp.net自定义控件(asp.net学习笔记四)
- SharePoint 应用的开发学习笔记(二)
- Java软件开发学习笔记(四)
- 开发asp.net自定义控件(asp.net学习笔记二)
- 游戏开发学习笔记(一)--采用何种语言开发
- 开发asp.net自定义控件(asp.net学习笔记三)
- 嵌入式开发学习笔记--第一课
- Java软件开发学习笔记(一)
- 用S60操作系统SDK开发NOKIA手机应用程序-学习笔记(3)
- Java软件开发学习笔记(一)
- .net开发平台体系结构学习笔记
- 数据库开发技术与工程实践 学习笔记
- J2ME学习笔记(四)-----用特定的MIDP API开发MIDlets
- Java软件开发学习笔记(三)
- SharePoint 应用的开发学习笔记(-)
- (Eclipse 学习笔记)在Eclipse中用myEclipse进行开发
- 用S60操作系统SDK开发NOKIA手机应用程序-学习笔记(1)
- 《Web Service 编程 --用C#.NET 开发网络服务》北京希望出版社 我的学习笔记(第一章)(也就是书上抄了一写东西而已)