由于公司业务需要,开发微信版本,才开始接触微信公众平台。在github折腾了几天,试过好几个微信sdk,最终选择fastweixin。个人觉得这个框架还是值得使用的,使用也简单。那么问题来了,很多人想使用fastweixin,苦于没有详细开发教程(官方只给出最简单的接入而已),本文是更为详细的微信接入开发教程,适合对微信有一定了解的开发者。
0.项目地址
项目主页:https://github.com/sd4324530/fastweixin
开源中国主页:http://git.oschina.net/pyinjava/fastweixin
csdn主页:https://code.csdn.net/sd4324530/fastweixin
1.对于maven项目,引用一下地址即可
<!-- fastweixin -->
<dependency>
<groupId>com.github.sd4324530</groupId>
<artifactId>fastweixin</artifactId>
<version>1.3.12</version>
</dependency>
2. 整合spring,将APIConfig单例管理,这里交由spring管理,配置如下;
application.properties 全局属性文件,将微信公众号信息配置进去
weixin.appid=APPID
weixin.secret=APP_SECRET
weixin.token=TOKEN
applicationContext.xml spring配置文件
<context:property-placeholder location="classpath:application.properties" /> <bean id="apiConfig" class="com.linkcm.weixin.api.config.ApiConfig"> <constructor-arg name="appid" value="${weixin.appid}" /> <constructor-arg name="secret" value="${weixin.secret}" /> </bean>
到这里为止,已经和spring整合完毕啦!
接下来,接入微信服务器准备,整合springmvc。
3. 整合springmvc
新建类WXController.java,继承weixinSupport,并实现相关方法,如果需要响应用户的消息/事件推送,可以直接重写相关方法,下面代码重写了几个常用的方法,加上少量的业务逻辑。
具体实现如下:
/** * 微信服务器入口 * * @author zenglong * @date 2016年8月19日 上午9:32:41 */ @Controller @RequestMapping("/wx") public class WXController extends WeixinSupport { @Value("${weixin.token}") private String token; @Autowired private ApiConfig config; @Override protected String getToken() { return token; } @Override protected String getAppId() { return config.getAppid(); } /** * 绑定服务器-get * * @param request * @param response * @return */ @RequestMapping(value = "/bind", method = RequestMethod.GET) @ResponseBody public String bind(HttpServletRequest request, HttpServletResponse response) { if (isLegal(request)) { // 绑定微信服务器成功 return request.getParameter("echostr"); } else { // 绑定微信服务器失败 return ""; } } /** * 处理数据请求-post * * @param request * @param response * @return */ @RequestMapping(value = "/bind", method = RequestMethod.POST, produces = MediaType.TEXT_XML_VALUE + ";charset=utf-8") @ResponseBody public String process(HttpServletRequest request, HttpServletResponse response) { log.debug("数据请求 S ..."); if (isLegal(request)) { String result = processRequest(request); log.debug("数据请求 E ... ," + result); return result; } else { log.debug("数据请求 E ... ," + "空处理"); return ""; } } /********************** 消息处理 S ***************************/ /** * 文本消息 */ @Override protected BaseMsg handleTextMsg(TextReqMsg msg) { // TODO Auto-generated method stub log.debug("文本消息..."); // 回复文本 // BaseMsg baseMsg = new TextMsg("你发送的是文本,内容为:" + msg.getContent() + // toEmoji(0x1F604)); // baseMsg.setCreateTime(new Date().getTime()); // baseMsg.setMsgType(ReqType.TEXT); // 回复图文等 // MediaAPI mediaAPI = new MediaAPI(config); // UploadMediaResponse uploadMediaResponse = mediaAPI.uploadMedia( // com.linkcm.weixin.api.enums.MediaType.IMAGE, // new File("C:/Users/username/Desktop/images/putty.jpg")); // String mediaId = uploadMediaResponse.getMediaId(); String mediaId4ikey = "59DJfSyZPO4fFIwedIews8iXFvsRBm3TzetDcuZz23d2Hg21G85Lae1GmCDfaCJd"; String mediaId4finance = "GR4M2oXuim2sQghkq42Yc4k1UbMvPcuItgbHpWJRwXwfGMG2kOSr_klmQ_lY_gZX"; String media_id = "yknEZ5F9yhQVHxbNWsGjyjVaixeV2vjzhjUPi12PglyH_rOm4zBJgy6MpkrvbWRw"; String fileUrl = "http://a.hiphotos.baidu.com/image/h%3D360/sign=c6c7e73ebc389b5027ffe654b535e5f1/a686c9177f3e6709392bb8df3ec79f3df8dc55e3.jpg"; Article article = new Article("源码在线首页", // 图文消息的作者 "我的图文描述", // 图文消息的描述 fileUrl, // 在图文消息页面点击“阅读原文”后的页面 "http://vivof.com"); Article article2 = new Article( "NodeJs首页", "Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境", fileUrl, "http://www.baidu.com"); Article article3 = new Article("Vue.js", "图文消息的描述 ", fileUrl, "http://www.baidu.com"); BaseMsg baseMsg = new NewsMsg( Arrays.asList(article, article2, article3)); return baseMsg; } /** * 位置消息 */ @Override protected BaseMsg handleLocationMsg(LocationReqMsg msg) { // TODO Auto-generated method stub log.debug("位置消息..."); BaseMsg baseMsg = new TextMsg("你发送的是地理位置 " + toEmoji(0x1F604) + " (" + msg.getLocationX() + "," + msg.getLocationY() + ")"); baseMsg.setCreateTime(new Date().getTime()); baseMsg.setMsgType(ReqType.TEXT); return baseMsg; } /** * 链接消息 */ @Override protected BaseMsg handleLinkMsg(LinkReqMsg msg) { // TODO Auto-generated method stub log.debug("链接消息..."); return super.handleDefaultMsg(msg); } /********************** 消息处理 E ***************************/ /********************** 事件处理 S ***************************/ /** * 关注事件 */ @Override protected BaseMsg handleSubscribe(BaseEvent event) { // TODO Auto-generated method stub log.debug("关注事件..."); BaseMsg baseMsg = new TextMsg("欢迎关注源码在线服务,").addLink("点这里了解更多内容", "http://vivof.com"); return baseMsg; } /** * 取消关注事件 */ @Override protected BaseMsg handleUnsubscribe(BaseEvent event) { // TODO Auto-generated method stub log.debug("取消关注事件..."); return super.handleUnsubscribe(event); } /** * 点击事件 */ @Override protected BaseMsg handleMenuClickEvent(MenuEvent event) { // TODO Auto-generated method stub log.debug("单击事件..."); String openid = event.getFromUserName(); log.info("用户openid : " + openid); GetUserInfoResponse userInfo = new UserAPI(config).getUserInfo(openid); String unionid = userInfo.getUnionid(); log.debug("openid 获取 unionid : " + unionid); // ... 业务逻辑,根据用户openid/unionid判断权限 if (authentication(unionid)) { // BaseMsg baseMsg = new TextMsg("身份验证通过"); log.info("身份验证通过..."); String fileUrl = "http://a.hiphotos.baidu.com/image/h%3D360/sign=c6c7e73ebc389b5027ffe654b535e5f1/a686c9177f3e6709392bb8df3ec79f3df8dc55e3.jpg"; Article article = new Article("源码在线首页", // 图文消息的作者 "我的图文描述", // 图文消息的描述 fileUrl, // 在图文消息页面点击“阅读原文”后的页面 "http://vivof.com"); Article article2 = new Article( "NodeJs首页", // 图文消息的作者 "Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境", fileUrl, "http://www.baidu.com"); Article article3 = new Article("Vue.js", "图文消息的描述 ", fileUrl, "http://www.baidu.com"); BaseMsg baseMsg = new NewsMsg(Arrays.asList(article, article2, article3)); return baseMsg; } else { BaseMsg baseMsg = new TextMsg("身份验证失败"); baseMsg.setMsgType(ReqType.TEXT); return baseMsg; } } /** * 跳转事件 */ @Override protected BaseMsg handleMenuViewEvent(MenuEvent event) { // TODO Auto-generated method stub log.debug("跳转事件..."); String openid = event.getFromUserName(); log.info("用户openid : " + openid); GetUserInfoResponse userInfo = new UserAPI(config).getUserInfo(openid); log.debug("openid 获取 unionid : " + userInfo.getUnionid()); // ... 自己的业务逻辑 BaseMsg baseMsg = new TextMsg("文本消息"); baseMsg.setMsgType(ReqType.TEXT); return baseMsg; } /** * 地理位置事件 */ @Override protected BaseMsg handleLocationEvent(LocationEvent event) { // TODO Auto-generated method stub log.debug("位置事件..."); return super.handleLocationEvent(event); } /** * 扫描事件 */ @Override protected BaseMsg handleScanCodeEvent(ScanCodeEvent event) { // TODO Auto-generated method stub log.debug("扫描事件..."); return super.handleScanCodeEvent(event); } /** * 消息模版 */ @Override protected BaseMsg handleTemplateMsgEvent(TemplateMsgEvent event) { // TODO Auto-generated method stub log.debug("消息模版..."); return super.handleTemplateMsgEvent(event); } /********************** 事件处理 E ***************************/ /********************** emoji表情 S ***************************/ /** * 发送emoji表情 * * @param hexEmoji * @return */ private String toEmoji(int hexEmoji) { log.debug("发送emoji表情..."); return String.valueOf(Character.toChars(hexEmoji)); } /********************** emoji表情 E ***************************/ /********************** 帮助方法 S ***************************/ /** * 微信用户根据unionid身份认证 * * @param unionid * @return */ private boolean authentication(String unionid) { log.debug("身份认证开始..."); WechatUser wechatUser = null; // ... 业务逻辑,如查数据库匹配信息 // WechatUser wechatUser = userService.getWechatUser(unionid); log.debug("微信用户信息:" + wechatUser); if (null != wechatUser && StringUtils.isNotBlank(wechatUser.getNickname())) { log.debug("身份认证通过..."); return true; } else { log.debug("身份认证失败..."); return false; } } /********************** 帮助方法 E ***************************/ /************************* 页面跳转 S ******************************/ @RequestMapping("/callback/page/{page}") public void toPge(@PathVariable String page, String code, HttpServletResponse response) throws Exception { log.info("即将跳转到 页面: " + page); OauthAPI api = new OauthAPI(config); OauthGetTokenResponse token = null; try { token = api.getToken(code); } catch (Exception e) { log.info("获取token失败..."); throw new APIException(e.getMessage()); } log.info("token : " + token); // .. 根据业务逻辑跳转 redirect(page, response); } /** * 页面回调路由 * * @return */ private Map<String, String> initPageMapping() { String basePath = "http://hsk20160127.imwork.net/finance-wap/"; Map<String, String> map = new HashMap<String, String>(); map.put("sub11", basePath + "card/cardfolder/0.html"); map.put("sub12", basePath + "card/cardfolder/0.html"); map.put("sub13", basePath + "folder/0.html"); map.put("sub14", basePath + "folder/1.html"); map.put("sub15", basePath + "folder/2.html"); map.put("sub21", basePath + "macroeconomy"); map.put("sub22", basePath + "industrydata"); return map; } /** * 根据路由进行页面跳转 * * @param page * @param response * @throws PageException * @throws IOException */ private void redirect(String page, HttpServletResponse response) throws Exception { Map<String, String> pageMapping = initPageMapping(); if (pageMapping.containsKey(page)) { response.sendRedirect(pageMapping.get(page)); } else { // 页面参数错误 throw new PageException("回调页面参数错误"); } } /************************* 页面跳转 E ******************************/ }
这样就整合springmvc完毕。
接下来就是相关设置与本地环境的搭建了。
4. 域名解析
花生壳域名解析还是可以的。去官网注册一个,好像好几块钱什么费用就可以免费使用了,感觉还是不错的。设置如下
这样就可以使用网址 http://hsk20160127.imwork.net/weixin 来访问项目了。
说明:虽然微信开发需要使用80端口(项目是8080端口),因为使用了花生壳,这些已经帮我们搞定了,外网80端口映射到本地的8080tomcat项目端口,我们只管使用网址访问我们的项目就可以。
5. 公众号设置
这里重点讲解fastweixin框架的使用,公众号设置就不在赘述。以下是简要叙述需要设置的项,文字不重要,看图。
5.1 接入地址
填写上面controller中的绑定方法bind()的get请求,也就是 /项目名/wx/bind,如果你使用花生壳等域名解析工具,访问完整路径为 http://XXX.imwork.net/项目名/wx/bind
填好,点击确定,发现绑定配置失败~~~~ 是不是项目还没启动呢 ^_^ ,继续往下看
5.2 授权回调地址
点击修改
确认即可。
5.3 js域名回调
这里没用上,可不填写。
6. 启动项目,因为花生壳映射的8080端口,所以设置tomcat的访问端口为8080,然后启动项目。回到回调地址页面,点击确定即可接入成功。
OK。觉得有帮助记得给个赞哈 ^_^。
如有疑问可联系QQ:303483090 ,一起讨论交流。