这篇文章转自《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. foreach嵌套循环

    最近几天被这个嵌套搞晕了,还好经过几天的努力终于解决了,特记录下,因为要传两个List集合到jsp页面,还都是在一起输出,发现不能把两个集合放在一个foreach,所以就写了两个foreach来接受, ...

  2. s:textarea 标签不能改变大小的解决方案

    在s标签写的form中,无法利用rows="50" cols="75"来改变s:textarea大小,cssClass也不管用时: 直接用普通的textarea ...

  3. 鸟哥Linux学习笔记07

    1, vi 是 老式的文字处理器,不过功能已经很齐全了,但是还是有可以进步的地方. vim可以说是程序开发者的一项很好用的工具,vim官网(http://www.vim.org)自己也说vim是一个“ ...

  4. JPA关系映射之one-to-many和many-to-one

    one-to-many(一对多)和many-to-one(多对一)双向关联 假设部门与员工是一对多关系,反过来员工与部门就是多对一关系. Dept.java类 public class Dept im ...

  5. 部署maria数据库到linux(源码编译安装)

    maria数据库是mysql原作者另外开发的一个版本,使用方法和mysql一样,可以直接用mysql的库连接. 在这下载包并解压: https://mariadb.org/download/ 建立数据 ...

  6. vue实例讲解之vue-router的使用

    实例讲解系列之vue-router的使用 先总结一下vue-router使用的基本框架: 1.安装并且引入vue-router 安装:npm install vue-router --save-dev ...

  7. 修改host可以上的一些网站

    打开路径 C:\Windows\System32\drivers\etc 博客园 223.6.248.220 www.cnblogs.com github 192.30.253.112 github. ...

  8. uvalive 3029 City Game

    https://vjudge.net/problem/UVALive-3029 题意: 给出一个只含有F和R字母的矩阵,求出全部为F的面积最大的矩阵并且输出它的面积乘以3. 思路: 求面积最大的子矩阵 ...

  9. Scala 中的隐式转换和隐式参数

    隐式定义是指编译器为了修正类型错误而允许插入到程序中的定义. 举例: 正常情况下"120"/12显然会报错,因为 String 类并没有实现 / 这个方法,我们无法去决定 Stri ...

  10. 数据的分类-JavaScript数据类型

    JavaScript数据类型 1.数据类型是什么? 我们接触的绝大多数程序语言来说,把数据都进行了分类,包括数字.字符.逻辑真假:int,long,string,boolean....等等:我们都知道 ...