这篇文章转自《http://www.qtdebug.com/spring-weixin/》

微信有两种模式,编辑模式和开发者模式,有些功能是互斥的,不可以同时使用,微信开发需要在开发者模式下进行(开发者模式下仍然可以去微信的网页上群发消息)。下面介绍的功能能满足大部分的需求,响应文本消息,图文消息,创建菜单,响应菜单消息等。

我们给微信提供服务有两种消息模式,被动和主动

  • 被动: 例如用户输入文本,点击菜单,微信服务器会访问我们的 Web 服务对应的 URL,我们返回对应的消息给微信服务器
  • 主动: 例如创建菜单,群发消息,这种模式需要我们主动去触发,给微信服务器发送消息,可以是执行某个定时任务触发,或者我们访问某个 URL 然后在其响应的代码里触发
  • 几个关键点

    • 微信服务器和我们的服务器绑定验证时使用 GET 发送一个带有 echostr 参数的请求
    • 其他消息使用的是 POST,常用的消息有
      1. 事件消息 event: subscribe, unsubscribe, location 等
      2. 文本消息 text: 可以回复文本,链接,图文
      3. 点击菜单回复的 click
      4. 点击菜单跳转为 view
    • 微信服务器访问我们的服务器的 URL 只有一个,就是在配置页中配置的 URL
    • 使用 app_id + app_secret 从微信服务器获取 access_token,有效时间是 7200 秒
    • 微信的接口:用于我们主动的从微信服务器获取信息或者主动的向微信服务器写入信息

    准备外网能访问的域名

    • 使用 ngrok.cc 让本地服务能在外网访问,这样微信服务器才能访问到我们的 Web 服务器

    准备公众号

    1. 访问 https://mp.weixin.qq.com 注册开发者账号
    2. 登陆后到最下面 开发 > 基本配置 中启用开发者模式,并点击 修改配置 配置我们自己给微信提供服务的 Web 地址、Token 等,然后使用 appID和 appsecret 就可以进行开发了
      • 如果我们申请的是 个人订阅号,很多功能接口都没有,例如自定义菜单都没有,为了使用所有的功能进行开发练习,可以使用微信提供的 公众平台测试帐号开发 > 开发者工具 > 公众平台测试帐号 > 进入,就可以看到 appID 和 appsecret,也需要在这里配置给微信提供服务的 Web 地址,Token 等,然后在页面中部找到 请用微信扫描关注测试公众号,扫描关注即可
      • 查看公众号接口权限说明开发 > 开发者工具 > 开发者文档 > 进入 > 公众号接口权限说明
      • 个人订阅号 是不能进行认证的
      • Gradle 依赖

        1
        compile("com.github.sd4324530:fastweixin:1.3.11")

        Fastweixin 的 git: https://git.oschina.net/pyinjava/fastweixin

      • 给微信提供服务的 Controller

        1
        public class WeixinController extends WeixinControllerSupport

        消息响应

        在 WeixinController 中重载下面几个函数

        • 响应订阅消息

          1
          protected BaseMsg handleSubscribe(BaseEvent event)
        • 响应文本消息

          1
          protected BaseMsg handleTextMsg(TextReqMsg msg)
        • 响应点击菜单消息

          1
          protected BaseMsg handleMenuClickEvent(MenuEvent event)

        创建消息

        • 创建文本消息

          1
          new TextMsg("你好: <a href=\"http://www.baidu.com\">百度</a>");
        • 创建图文消息(单图文,多图文都可以)

          1
          2
          3
          4
          String picUrl = "http://image.17car.com.cn/image/20120810/20120810092133_13289.jpg"; // 消息中显示的图片
          String url = "http://news.17car.com.cn/saishi/20120810/336283.html"; // 点击消息后跳转的网页的地址
          String description = "700 马力道路赛车 DDMWorks 打造最强 Atom";
          NewsMsg msg = new NewsMsg(Arrays.asList(new Article("Atom", description, picUrl, url), new Article("Atom", description, picUrl, url)));

        创建菜单

      • 创建菜单

        • 微信不会访问这个URL,需要我们自己访问创建菜单的 URL,然后才能向微信发送创建菜单信息。
        • 微信只能保证菜单 24 小时之类生效,想要马上看到菜单效果,先取消关注,然后再次关注就可以了
        • CLICK 类型的菜单会发送消息到我们的服务器,handleMenuClickEvent() 进行响应,根据 key 来判断是哪个菜单被点击
        • VIEW 类型的菜单不回发送消息到我们的服务器,而是直接跳转到对应的 URL
        • 菜单分一级菜单和二级菜单
          • 最多有 3 个一级菜单,5 个二级菜单
          • 一级菜单最多 4 个汉字,二级菜单最多 7 个汉字
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        @GetMapping("/create-menu")
        @ResponseBody
        public String createMenu() {
        // 准备一级主菜单
        MenuButton main1 = new MenuButton();
        main1.setType(MenuType.CLICK); // 可点击的菜单
        main1.setKey("main1");
        main1.setName("主菜单一");
         
        MenuButton main2 = new MenuButton();
        main2.setType(MenuType.VIEW); // 链接的菜单,点击后跳转到对应的 URL
        main2.setName("主菜单二");
        main2.setUrl("http://www.baidu.com");
         
        MenuButton main3 = new MenuButton();
        main3.setType(MenuType.CLICK);
        main3.setName("真题");
         
        // 带有子菜单
        MenuButton sub1 = new MenuButton();
        sub1.setType(MenuType.CLICK); // 带有子菜单
        sub1.setName("2016 语文");
        sub1.setKey("sub1");
         
        MenuButton sub2 = new MenuButton();
        sub2.setType(MenuType.CLICK);
        sub2.setName("2016 数学");
        sub2.setKey("sub2");
        main3.setSubButton(Arrays.asList(sub1, sub2));
         
        Menu menu = new Menu();
        menu.setButton(Arrays.asList(main1, main2, main3));
         
        //创建菜单
        ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);
        MenuAPI menuAPI = new MenuAPI(config);
        ResultType resultType = menuAPI.createMenu(menu);
        return resultType.toString();
        }

        使用 Json 字符串创建菜单

        上面的程序创建菜单太麻烦了,可以使用 Json 字符串,然后反序列化为菜单对象,下面使用 Jackson 来实现。

        Jackson 依赖: compile("com.fasterxml.jackson.core:jackson-databind:2.7.4")

        菜单的 Json 字符串可以放在文件,数据库中等,方便修改,而且比使用对象的方式更直观,例如下面这样:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        {
        "button": [{
        "type": "CLICK",
        "name": "主菜单一",
        "key": "main1"
        }, {
        "type": "VIEW",
        "name": "主菜单二",
        "url": "http://www.baidu.com"
        }, {
        "type": "CLICK",
        "name": "真题",
        "subButton": [{
        "type": "CLICK",
        "name": "2016 语文",
        "key": "sub1"
        }, {
        "type": "CLICK",
        "name": "2016 数学",
        "key": "sub2"
        }]
        }]
        }
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        @GetMapping("/create-menu")
        @ResponseBody
        public String createMenu() throws Exception {
        String json = "{\n" +
        " \"button\": [{\n" +
        " \"type\": \"CLICK\",\n" +
        " \"name\": \"今日歌曲\",\n" +
        " \"key\": \"V1001_TODAY_MUSIC\"\n" +
        " }, {\n" +
        " \"name\": \"菜单\",\n" +
        " \"subButton\": [{\n" +
        " \"type\": \"VIEW\",\n" +
        " \"name\": \"搜索\",\n" +
        " \"url\": \"http://www.soso.com/\"\n" +
        " }, {\n" +
        " \"type\": \"VIEW\",\n" +
        " \"name\": \"视频\",\n" +
        " \"url\": \"http://v.qq.com/\"\n" +
        " }, {\n" +
        " \"type\": \"CLICK\",\n" +
        " \"name\": \"赞一下我们\",\n" +
        " \"key\": \"V1001_GOOD\"\n" +
        " }]\n" +
        " }]\n" +
        "}";
         
        ObjectMapper mapper = new ObjectMapper();
        Menu menu = mapper.readValue(json, Menu.class);
         
        //创建菜单
        ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);
        MenuAPI menuAPI = new MenuAPI(config);
        ResultType resultType = menuAPI.createMenu(menu);
        return resultType.toString();
        }

        示例程序

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        81
        82
        83
        84
        85
        86
        87
        88
        89
        90
        91
        92
        93
        94
        95
        96
        97
        98
        99
        100
        101
        102
        103
        104
        105
        106
        107
        108
        109
        110
        111
        112
        113
        114
        115
        116
        117
        118
        119
        120
        121
        122
        package com.xtuer.controller;
         
        import com.github.sd4324530.fastweixin.api.MenuAPI;
        import com.github.sd4324530.fastweixin.api.config.ApiConfig;
        import com.github.sd4324530.fastweixin.api.entity.Menu;
        import com.github.sd4324530.fastweixin.api.entity.MenuButton;
        import com.github.sd4324530.fastweixin.api.enums.MenuType;
        import com.github.sd4324530.fastweixin.api.enums.ResultType;
        import com.github.sd4324530.fastweixin.message.Article;
        import com.github.sd4324530.fastweixin.message.BaseMsg;
        import com.github.sd4324530.fastweixin.message.NewsMsg;
        import com.github.sd4324530.fastweixin.message.TextMsg;
        import com.github.sd4324530.fastweixin.message.req.MenuEvent;
        import com.github.sd4324530.fastweixin.message.req.TextReqMsg;
        import com.github.sd4324530.fastweixin.servlet.WeixinControllerSupport;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        import org.springframework.web.bind.annotation.GetMapping;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.ResponseBody;
        import org.springframework.web.bind.annotation.RestController;
         
        import java.util.Arrays;
         
        @RestController
        @RequestMapping("/weixin")
        public class WeixinController extends WeixinControllerSupport {
        private static final Logger LOG = LoggerFactory.getLogger(WeixinController.class);
        private static final String TOKEN = "xxxxxx"; // 你的 token
        private static final String APP_ID = "yyyyyy"; // 你的 appID
        private static final String APP_SECRET = "zzzzzz"; // 你的 appsecret
         
        // 重写父类方法,处理对应的微信消息
        @Override
        protected BaseMsg handleTextMsg(TextReqMsg msg) {
        String content = msg.getContent(); // content 是用户输入的信息
         
        switch (content.toUpperCase()) {
        case "URL":
        // 消息中有链接
        return new TextMsg("你好: <a href=\"http://www.baidu.com\">百度</a>");
        case "ATOM":
        // 图文消息
        String picUrl = "http://image.17car.com.cn/image/20120810/20120810092133_13289.jpg"; // 消息中显示的图片
        String url = "http://news.17car.com.cn/saishi/20120810/336283.html"; // 点击消息后跳转的网页的地址
        String description = "700 马力道路赛车 DDMWorks 打造最强 Atom";
        return new NewsMsg(Arrays.asList(new Article("Atom", description, picUrl, url), new Article("Atom", description, picUrl, url)));
        default:
        return new TextMsg("不识别的命令, 您输入的内容是: " + content);
        }
        }
         
        @Override
        protected BaseMsg handleMenuClickEvent(MenuEvent event) {
        String key = event.getEventKey();
        switch (key.toUpperCase()) {
        case "MAIN1":
        return new TextMsg("点击按钮");
        case "SUB1":
        return new TextMsg("2016 语文");
        case "SUB2":
        return new TextMsg("2016 数学");
        default:
        return new TextMsg("不识别的菜单命令");
        }
        }
         
        // 设置 TOKEN,用于绑定微信服务器
        @Override
        protected String getToken() {
        return TOKEN;
        }
         
        // 获取 access token: http://localhost:8080/weixin/access-token
        @GetMapping("/access-token")
        @ResponseBody
        public String getAccessToken() {
        ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);
        return config.getAccessToken();
        }
         
        // 创建菜单, 访问 http://localhost:8080/weixincreate-menu 就会把菜单信息发送给微信服务器
        @GetMapping("/create-menu")
        @ResponseBody
        public String createMenu() {
        // 准备一级主菜单
        MenuButton main1 = new MenuButton();
        main1.setType(MenuType.CLICK); // 可点击的菜单
        main1.setKey("main1");
        main1.setName("主菜单一");
         
        MenuButton main2 = new MenuButton();
        main2.setType(MenuType.VIEW); // 链接的菜单,点击后跳转到对应的 URL
        main2.setName("主菜单二");
        main2.setUrl("http://www.baidu.com");
         
        MenuButton main3 = new MenuButton();
        main3.setType(MenuType.CLICK); // 带有子菜单
        main3.setName("真题");
         
        // 带有子菜单
        MenuButton sub1 = new MenuButton();
        sub1.setType(MenuType.CLICK);
        sub1.setName("2016 语文");
        sub1.setKey("sub1");
         
        MenuButton sub2 = new MenuButton();
        sub2.setType(MenuType.CLICK);
        sub2.setName("2016 数学");
        sub2.setKey("sub2");
        main3.setSubButton(Arrays.asList(sub1, sub2));
         
        Menu menu = new Menu();
        menu.setButton(Arrays.asList(main1, main2, main3));
         
        //创建菜单
        ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);
        MenuAPI menuAPI = new MenuAPI(config);
        ResultType resultType = menuAPI.createMenu(menu);
        return resultType.toString();
        }
        }

Spring + Fastweixin 微信开发的更多相关文章

  1. 基于fastweixin的微信开发环境搭建(一)

    由于公司业务需要,开发微信版本,才开始接触微信公众平台.在github折腾了几天,试过好几个微信sdk,最终选择fastweixin.个人觉得这个框架还是值得使用的,使用也简单.那么问题来了,很多人想 ...

  2. 分享 Java微信开发SDK

    分享 Java微信开发SDK •发布于 4周前  •作者 朋也  •432 次浏览  •最后一次编辑是 2周前  •来自 分享 给大家分享两个java开发微信公众号的sdk jfinal-weixin ...

  3. spring boot + vue + element-ui全栈开发入门——spring boot后端开发

    前言 本文讲解作为后端的spring boot项目开发流程,如果您还不会配置spring boot环境,就请点击<玩转spring boot——快速开始>,如果您对spring boot还 ...

  4. 微信开发(一)基于Wx-java的微信分享功能

    最近在做微信服务号开发,简单总结一下,便于自己学习积累和分享给大家: 环境介绍: Spring+ Spring MVC +Mybatis 开发语言: JAVA 微信公众平台的开发中,微信只公布了一个基 ...

  5. 微信开发之如何使用开发工具--weixin-java-tools

    一.前沿 微信公众平台由于没有提供针对语言的开发包,只公布了一个基于Http协议的接口和加解密的算法sdk,这样给微信公众号的开发者带来很多工作量,除了实现业务逻辑外,还需要自己处理底层的接口协议细节 ...

  6. SSM到Spring Boot-从零开发校园商铺平台

    第1章 开发准备 本章包含课程介绍,同时讲解开发网站所需要准备的事情,并且带领大家从零开始搭建一个Maven Web. 1-1 课程导学 1-2 开发准备 第2章 项目设计和框架搭建 本章主要先带领大 ...

  7. 10个Spring Boot快速开发的项目,接私活利器(快速、高效)

    本文为大家精选了 码云 上优秀的 Spring Boot 语言开源项目,涵盖了企业级系统框架.文件文档系统.秒杀系统.微服务化系统.后台管理系统等,希望能够给大家带来一点帮助:) 1.项目名称:分布式 ...

  8. 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!

    写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...

  9. 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则

    写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...

随机推荐

  1. vim基础详解

    目录: 什么是vim Vim能做什么 如何学习vim 如何用vim打开一个文件 Vim的三种模式 插入模式 命令模式 扩展命令模式 光标移动 在命令模式下 删除,复制,粘贴 扩展命令模式 可视化模式 ...

  2. JavaEE成长之路

    前言 学习Java已经有一段时间了,在学习的过程中也走过了不少弯路. 写下这篇博文,主要是想记录下自己学习编程之路,以及反思自己在学习的时候出现的问题,下面也会给出我自认为学习JavaEE的路线,想要 ...

  3. 原型那些事 - JavaScript深入浅出(三)

    前两次总结了JavaScript中的基本数据类型(值类型<引用类型>,引用类型<复杂值>)以及他们在内存中的存储,对内存空间有了一个简单的了解,以及第二次总结了this深入浅出 ...

  4. 关于搭建php电商环境时缺少fileinfo、数据库安装出错问题解决办法

    今天以WSTMart电商系统为例讲解 搭建php电商环境缺少fileinfo.数据库安装出错问题找了很多方法都没能很好解决,该方法简单明了,容易操作 首先需要到开源中国中下载该系统源码,网址为:htt ...

  5. GitHub开源:升讯威微信营销系统(第三方微信平台)完整源代码

    GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction 升讯威微信营销系统开发实践系列升讯威微信营销系统开发实践:(1)功能设计与架构设 ...

  6. Markdown 编写规范

    说明及目的 作为一个在博客园混迹了俩三年的人,一直在这里看别人的博客,现在准备开始写自己的博客,目的呢,就是一下几点吧: 项目过程中的历史经验教训积累记载,吃一堑长一智,不想在同一个坑掉进去好几次 学 ...

  7. [Java语言] 《struts2和spring MVC》的区别_动力节点

    1.Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上Spr ...

  8. Webx项目的获取与验证

    在JavaWeb环境配置后就可进行Webx实例项目的获取与研读了. 1.创建一个初始的Demo工程. 1)下载 Webx Maven 项目的目录结构Artifact插件. archetype-webx ...

  9. CSS图片垂直居中方法整理集合

    原帖链接:http://bbs.blueidea.com/thread-2666987-1-1.html 1.因为Opera,FF3,IE8均支持display:talbe;这些特性了,因此改进的办法 ...

  10. FPGA IN 消费电子

    消费电子: 消费电子(Consumer electronics),指供日常消费者生活使用的电子产品.消费类电子产品是指用于个人和家庭与广播.电视有关的音频和视频产品,主要包括:电视机.影碟机(VCD. ...