微信公众账号开发教程.doc
微信公众帐号接口开发接触微信公众帐号已经有两个多月的时间了,在这期间,除了陆续完善个人公众帐号xiaoqrobot以外,还带领团队为公司开发了两个企业应用:一个是普通类型的公众帐号,另一个是会议类型的公众帐号。经过这3个公众帐号的开发,对目前微信公众平台开放的api算是比较熟悉了,像文本消息、图文消息、音乐消息、语音消息、位置消息等全部用到过,菜单也使用过。所以,就有了写微信公众帐号开发教程的想法,将学习到的技术经验分享出来,帮助更多需要的朋友,也希望借此认识同行的朋友,共同交流,共同进步!下面将对即将推出的微信公众帐号开发系列连载教程做简单的说明。教程主要是面向有一定Java编程基础的朋友,不打算从编程语言开始讲起,一是考虑到自己没有那么多时间和精力(要上班、装修、学车等),二是怕等我把编程语言讲完,微信公众帐号又发生了大变化,这样教程就显得有点过时,没有吸引力了,所以只能是有侧重点的介绍。至于内容方面,大概会涉及到:1)前沿知识:微信公众帐号的分类、两种模式各自的特点和区别、开发模式的配置使用等;2)API中各类消息的使用(我已经对api进行封装并打成了jar包,到时候会考虑分享出来);3)微信公众帐号开发中的小技巧(如换行、通过代码发送表情、屏幕飘雪花、表情的接收识别、在Android和iOS上表现不一致等等);4)与业务系统对接的方法(链接、短信等,除了技术讲解还会做一定的分析对比);5)微信公众平台上常见功能的开发(如像小黄鸡那样的人机对话、天气预报、精确的定位及百度地图的使用、音乐搜索、语音识别解析等)当然,具体写出来的内容肯定不止这些,但一定会包含以上介绍的所有内容。我也不知道多久能写完这些内容,当然是越快越好,我会尽全力的。希望正在看博文的你通过微信关注xiaoqrobot或者在博客留言支持,给我动力,谢谢!开发xiaoqrobot就是为了学习微信公众帐号开发,将api开放出来的各类消息都体验了。虽然现在看来有点大杂烩的意思,但还是比较实用的,一款生活、娱乐的好帮手,目前已有370多关注者。周边搜索功能定位比较准确(解决了纠偏问题,能精确到十米范围),平时出门在外搜美食、ATM机、厕所、超市等再方便不过了,还提供路线导航;聊天唠嗑功能是我自己开发的,后面的连载教程很多内容都会从中抽取出来,下面是主界面截图,对系列连载教程有所期待的朋友很建议关注体验下,做的不好的地方也请多提意见,除了技术本身外,体验也是我比较重视关注的。2. 微信公众帐号开发教程第2篇-微信公众帐号的类型(普通和会议) 个人公众帐号与企业公众帐号记得在两个月前,我在微信官方开发群里问个人公众帐号与企业公众帐号有什么区别的时候,还被人笑话过,没有人愿意告知,也许是这个问题问的太过于简单了吧。我想一定也还有不少朋友在刚接触时,也搞不清楚这一点。其实,在注册微信公众帐号时,是不区分个人帐号与企业帐号的,它们需要填写的注册资料是一样的,这个区别仅仅是帐号申请成功后在使用用途上的区别罢了。然而,在注册公众帐号时的确有个类型可以选择,但并不是选择个人帐号与企业帐号,那有些什么类型可以选择呢?这也正是今天我想讲的主题,请继续往下看。注册时可选择的两种帐号类型微信公众帐号注册的最后一步是填写“公众号信息”,最后一个选项是选择“类型”,它有二个值可供选择“普通公众帐号类型”和“公众会议帐号”。当我们选择“公众会议号”时,下方会出现醒目的红字“提醒:会议号是有一定时间限制的公众帐号,过期后将无法登录使用。”,如下图所示。那注册时到底应该选择哪个类型呢?这就需要我们对两种类型有一定的了解才好做出判断。下面将主要通过介绍公众会议帐号与普通公众帐号的区别来进行说明。公众帐号与普通帐号的区别在注册好的公会会议帐号的“设置”一栏里,可以看到“会议号设置”项,如下图所示:其实会议帐号与普通帐号的区别在“会议号设置”里就能全部体现出来,它们的区别有以下三点:1)有效时间普通帐号是创建后永久有效的,而会议帐号的有效期只有一个月,一个月后帐号就失效了。帐号失效后登录微信公众平台时,会提示“该公众会议号已经过期,无法再登录使用”,如下图所示:帐号失效后已关注了会议帐号的用户继续使用时,会提示“该公众帐号已过期,无法下发消息”,但如果是有菜单权限的会议帐号,仍然可以通过菜单获取信息,帐号过期后菜单的响应没有被禁止,如下图所示:从上图可以看到,会议帐号过期后,无法再通过文本获取消息,但点击菜单是可以继续使用的,图中的图文消息“峰会概况”就是点击菜单后返回的。2)关注权限普通帐号任何人都可以关注,没有权限限制。会议帐号是可以设置关注权限的,分为两种:任何人都可以关注和需要通过验证才可以关注,不进行此项设置时默认是前者。如果设置为需要通过验证才可以关注,就有点类似于微信添加朋友时的验证一样,只不过这里的验证问题是可以设置的,并且如果你设置的验证消息是类似于询问用户身份的,例如“请问您的真实姓名叫什么?”,你还可以勾选“将验证消息作为备注名”,这样就很好辩认所有关注了会议帐号的人。3)参与人相互可见普通帐号的关注者之间是不可见的,而会议帐号的关注者之间是相互可见的,这是什么意思呢?在会议号设置里,如果勾选了“参与人相互可见”,那么在关注了该会议帐号后,能够在帐号详细资料里看到多了一项“与会者”,点击它将会显示所有关注了该会议帐号的微信号列表,并且点击某个参与人还可以查看详细资料、申请加为朋友等。这是会议帐号比较给力的一个功能,方便参加会议的人相互认识。以上三点是会议帐号的特点,也是与普通帐号的区别。可以看出,会议帐号是在普通帐号功能的基础上增加了帐号有效时间限制(一个月)、关注权限和关注者相互可见三个功能。其实,微信目前对会议帐号的支持还远远不够。比如像会议主题、时间、地点等会议的常规属性设置都不支持,还有会议通常都会有的签到、互动、投票等环节也没有任何体现,更没有考虑到周期性的会议,希望微信后期的版本对这块的支持力度更大。3. 微信公众帐号开发教程第3篇-开发模式启用及接口配置 编辑模式与开发模式微信公众帐号申请成功后,要想接收处理用户的请求,就必须要在“高级功能”里进行配置,点击“高级功能”,将看到如下界面:从上图中可以看到,高级功能包含两种模式:编辑模式和开发模式,并且这两种模式是互斥关系,即两种模式不能同时开启。那两种模式有什么区别呢?作为开发人员到底要开启哪一种呢?编辑模式:主要针对非编程人员及信息发布类公众帐号使用。开启该模式后,可以方便地通过界面配置“自定义菜单”和“自动回复的消息”。开发模式:主要针对具备开发能力的人使用。开启该模式后,能够使用微信公众平台开放的接口,通过编程方式实现自定义菜单的创建、用户消息的接收/处理/响应。这种模式更加灵活,建议有开发能力的公司或个人都采用该模式。启用开发模式(上)微信公众帐号注册完成后,默认开启的是编辑模式。那么该如何开启开发模式呢?操作步骤如下:1)点击进入编辑模式,将右上角的编辑模式开关由“开启”切换到“关闭”,如下图所示:2)点击高级功能进入到开发模式,将右上角的开发模式开关由“关闭”切换到“开启”,但在切换时会遇到如下提示:提示需要我们先成为开发者,才能开启开发模式。那就先点击下图所示的“成为开发者”按钮:如果提示资料不全,那就先补齐资料再回来继续操作。需要补全的资料有公众帐号头像、描述和运营地区。待资料补全后,再次点击“成为开发者”,这时将看到接口配置信息界面,如下图所示:这里需要填写URL和Token两个值。URL指的是能够接收处理微信服务器发送的GET/POST请求的地址,并且是已经存在的,现在就能够在浏览器访问到的地址,这就要求我们先把公众帐号后台处理程序开发好(至少应该完成了对GET请求的处理)并部署在公网服务器上。Token后面会详细说明。也就是说要完成接口配置,只需要先完成微信服务器的GET请求处理就可以?是的。 那这是为什么呢?因为这是微信公众平台接口中定义的。具体请参考API文档-消息接口-消息接口指南中的网址接入部分。点此进入。上面写的很清楚,其实你只要能理解上面在说什么就OK了,至于怎么编写相关代码,我已经帮你完成了,请继续往下看。创建公众帐号后台接口程序创建一个Java Web工程,并新建一个能够处理请求的Servlet,命名任意,我在这里将其命名为org.liufeng.course.servlet.CoreServlet,代码如下:java view plaincopyprint?1. packageorg.liufeng.course.servlet;2. 3. importjava.io.IOException;4. importjava.io.PrintWriter;5. 6. importjavax.servlet.ServletException;7. importjavax.servlet.http.HttpServlet;8. importjavax.servlet.http.HttpServletRequest;9. importjavax.servlet.http.HttpServletResponse;10. 11. importorg.liufeng.course.util.SignUtil;12. 13. /*14. *核心请求处理类15. *16. *authorliufeng17. *date2013-05-1818. */19. publicclassCoreServletextendsHttpServlet20. privatestaticfinallongserialVersionUID=4440739483644821986L;21. 22. /*23. *确认请求来自微信服务器24. */25. publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException26. /微信加密签名 27. Stringsignature=request.getParameter("signature");28. /时间戳 29. Stringtimestamp=request.getParameter("timestamp");30. /随机数 31. Stringnonce=request.getParameter("nonce");32. /随机字符串 33. Stringechostr=request.getParameter("echostr");34. 35. PrintWriterout=response.getWriter();36. /通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败 37. if(SignUtil.checkSignature(signature,timestamp,nonce)38. out.print(echostr);39. 40. out.close();41. out=null;42. 43. 44. /*45. *处理微信服务器发来的消息46. */47. publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException48. /TODO消息的接收、处理、响应 49. 50. 51. package org.liufeng.course.servlet;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.liufeng.course.util.SignUtil;/* * 核心请求处理类 * * author liufeng * date 2013-05-18 */public class CoreServlet extends HttpServlet private static final long serialVersionUID = 4440739483644821986L;/* * 确认请求来自微信服务器 */public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException / 微信加密签名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 (SignUtil.checkSignature(signature, timestamp, nonce) out.print(echostr);out.close();out = null;/* * 处理微信服务器发来的消息 */public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException / TODO 消息的接收、处理、响应可以看到,代码中只完成了doGet方法,它的作用正是确认请求是否来自于微信服务器;而doPost方法不是我们这次要讲的内容,并且完成接口配置也不需要管doPost方法,就先空在那里。 在doGet方法中调用了org.liufeng.course.util.SignUtil.checkSignature方法,SignUtil.java的实现如下:java view plaincopyprint?1. packageorg.liufeng.course.util;2. 3. importjava.security.MessageDigest;4. importjava.security.NoSuchAlgorithmException;5. importjava.util.Arrays;6. 7. /*8. *请求校验工具类9. *10. *authorliufeng11. *date2013-05-1812. */13. publicclassSignUtil14. /与接口配置信息中的Token要一致 15. privatestaticStringtoken="weixinCourse"16. 17. /*18. *验证签名19. *20. *paramsignature21. *paramtimestamp22. *paramnonce23. *return24. */25. publicstaticbooleancheckSignature(Stringsignature,Stringtimestamp,Stringnonce)26. Stringarr=newStringtoken,timestamp,nonce;27. /将token、timestamp、nonce三个参数进行字典序排序 28. Arrays.sort(arr);29. StringBuildercontent=newStringBuilder();30. for(inti=0;i<arr.length;i+)31. content.append(arri);32. 33. MessageDigestmd=null;34. StringtmpStr=null;35. 36. try37. md=MessageDigest.getInstance("SHA-1");38. /将三个参数字符串拼接成一个字符串进行sha1加密 39. bytedigest=md.digest(content.toString().getBytes();40. tmpStr=byteToStr(digest);41. catch(NoSuchAlgorithmExceptione)42. e.printStackTrace();43. 44. 45. content=null;46. /将sha1加密后的字符串可与signature对比,标识该请求来源于微信 47. returntmpStr!=null?tmpStr.equals(signature.toUpperCase():false;48. 49. 50. /*51. *将字节数组转换为十六进制字符串52. *53. *parambyteArray54. *return55. */56. privatestaticStringbyteToStr(bytebyteArray)57. StringstrDigest=""58. for(inti=0;i<byteArray.length;i+)59. strDigest+=byteToHexStr(byteArrayi);60. 61. returnstrDigest;62. 63. 64. /*65. *将字节转换为十六进制字符串66. *67. *parammByte68. *return69. */70. privatestaticStringbyteToHexStr(bytemByte)71. charDigit=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F;72. chartempArr=newchar2;73. tempArr0=Digit(mByte>>>4)&0X0F;74. tempArr1=DigitmByte&0X0F;75. 76. Strings=newString(tempArr);77. returns;78. 79. package org.liufeng.course.util;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Arrays;/* * 请求校验工具类 * * author liufeng * date 2013-05-18 */public class SignUtil / 与接口配置信息中的Token要一致private static String token = "weixinCourse"/* * 验证签名 * * 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(arri);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;/* * 将字节数组转换为十六进制字符串 * * param byteArray * return */private static String byteToStr(byte byteArray) String strDigest = ""for (int i = 0; i < byteArray.length; i+) strDigest += byteToHexStr(byteArrayi);return strDigest;/* * 将字节转换为十六进制字符串 * * param mByte * return */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 char2;tempArr0 = Digit(mByte >>> 4) & 0X0F;tempArr1 = DigitmByte & 0X0F;String s = new String(tempArr);return s;这里唯一需要注意的就是SignUtil类中的成员变量token,这里赋予什么值,在接口配置信息中的Token就要填写什么值,两边保持一致即可,没有其他要求,建议用项目名称、公司名称缩写等,我在这里用的是项目名称weixinCourse。 最后再来看一下web.xml中,CoreServlet是怎么配置的,web.xml中的配置代码如下:html view plaincopyprint?1. <?xmlversion="1.0"encoding="UTF-8"?>2. <web-appversion="2.5"xmlns="http:/java.sun.com/xml/ns/javaee"3. xmlns:xsi="http:/www.w3.org/2001/XMLSchema-instance"4. xsi:schemaLocation="http:/java.sun.com/xml/ns/javaee5. http:/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">6. <servlet>7. <servlet-name>coreServlet</servlet-name>8. <servlet-class>9. org.liufeng.course.servlet.CoreServlet10. </servlet-class>11. </servlet>12. 13. <!-url-pattern中配置的/coreServlet用于指定该Servlet的访问路径->14. <servlet-mapping>15. <servlet-name>coreServlet</servlet-name>16. <url-pattern>/coreServlet</url-pattern>17. </servlet-mapping>18. 19. <welcome-file-list>20. <welcome-file>index.jsp</welcome-file>21. </welcome-file-list>22. </web-app><?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http:/java.sun.com/xml/ns/javaee"xmlns:xsi="http:/www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http:/java.sun.com/xml/ns/javaee http:/java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><servlet><servlet-name>coreServlet</servlet-name><servlet-class>org.liufeng.course.servlet.CoreServlet</servlet-class></servlet><!- url-pattern中配置的/coreServlet用于指定该Servlet的访问路径 -><servlet-mapping><servlet-name>coreServlet</servlet-name><url-pattern>/coreServlet</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>到这里,所有编码都完成了,就是这么简单。接下来就是将工程发布到公网服务器上,如果没有公网服务器环境,可以去了解下BAE、SAE或阿里云。发布到服务器上后,我们在浏览器里访问CoreServlet,如果看到如下界面就表示我们的代码没有问题: 啊,代码都报空指针异常了还说证明没问题?那当然了,因为直接在地址栏访问coreServlet,就相当于提交的是GET请求,而我们什么参数都没有传,在验证的时候当然会报空指针异常。接下来,把coreServlet的访问路径拷贝下来,再回到微信公众平台的接入配置信息界面,将coreServlet的访问路径粘贴到URL中,并将SignUtil类中指定的token值weixinCourse填入到Token中,填写后的结果如下图所示:我在写这篇教程的时候是使用的BAE环境,如果想学习微信公众帐号开发又没有公网服务器环境的,建议可以试试,注册使用都很方便,如果有问题我们还可以交流。接着点击“提交”,如果程序写的没问题,并且URL、Token都填写正确,可以在页面最上方看到“提交成功”的提示,并会再次跳转到开发模式设置界面,而且能够看到“你已成为开发者”的提示,如下图所示:启用开发模式(下)这个时候就已经成为开发者了,百般周折啊,哈哈,到这里还没有完哦,还有最后一步工作就是将开发模式开启。将右上角的开发模式开关由“关闭”切换到“开启”,如下图所示:到这里,接口配置、开发模式的开启就都完成了,本章节的内容也就讲到这里。接下来要章节要讲的就是如何接收、处理、响应由微信服务器转发的用户发送给公众帐号的消息,也就是完成CoreServlet中doPost方法的编写。4. 微信公众帐号开发教程第4篇-消息及消息处理工具的封装工欲善其事必先利其器!本篇内容主要讲解如何将微信公众平台定义的消息及消息相关的操作封装成工具类,方面后期的使用。这里需要明确的是消息其实是由用户发给你的公众帐号的,消息先被微信平台接收到,然后微信平台会将该消息转给你在开发模式接口配置中指定的URL地址。微信公众平台消息接口要接收微信平台发送的消息,我们需要先熟悉微信公众平台API中消息接口部分,点此进入,点击后将进入到消息接口指南部分,如下图所示:在上图左侧可以看到微信公众平台目前开放的接口有三种:消息接口、通用接口和自定义菜单接口。通用接口和自定义菜单接口只有拿到内测资格才能调用,而内测资格的申请也已经关闭了,我们只有期待将来某一天微信会对大众用户开放吧,所以没有内测资格的用户就不要再浪费时间在这两个接口上,只需要用好消息接口就可以了。消息推送和消息回复下面将主要介绍消息接口。对于消息的接收、响应我们只需要关注上图中的“4 消息推送”和“5 消息回复”就足够了。我们先来了解接口中的“消息推送”指的是什么,点击“4 消息推送”,可以看到接口中的“消息推送”指的是“当普通用户向公众帐号发消息时,微信服务器将POST该消息到填写的URL上”,即这里定义的是用户能够发送哪些类型的消息、消息有哪些字段、消息被微信服务器以什么方式转发给我们的公众帐号后台。消息推送中定义了我们将会接收到的消息类型有5种:文本消息、图片消息、地理位置消息、链接消息和事件推送,其实语音消息我们也能够接收到的,只不过拿不到具体的语音文件而以(需要内测资格才能够获取语音文件)。接口中的“消息回复”定义了我们能回复给用户的消息类型、消息字段和消息格式,微信公众平台的接口指南中是这样描述的:上面说到我们能回复给用户的消息有5种,但目前在开发模式下能回复的消息只有3种:文本消息、音乐消息和图文消息,而语音消息和视频消息目前只能在编辑模式下使用。消息的封装接下来要做的就是将消息推送(请求)、消息回复(响应)中定义的消息进行封装,建立与之对应的Java类(Java是一门面向对象的编程语言,封装后使用起来更方便),下面的请求消息是指消息推送中定义的消息,响应消息指消息回复中定义的消息。请求消息的基类把消息推