这篇文章转自《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. PowerDesigner连接MySQL和逆向工程图

    0.写在前面的话 最近想梳理公司项目的表间关系,从项目后台管理系统的操作入手,以及代码的hibernate注解入手,都不算特别尽人意,于是最后还是鼓捣了一下PowerDesigner的逆向工程图,这样 ...

  2. Python学习笔记008_类_对象_继承_组合_类相关的BIF

    # 对象 = 属性 + 方法>>> # Python中的类名约定以大写字母开始>>> # tt = Turtle() 这就是创建类实例的方法,其它语言用new ,它 ...

  3. gephi安装后无法打开

    具体解决的方法是找到gephi.conf文件(在“gephi安装目录\etc”中)文件,添加下面的一行,指定jdkhome的路径. jdkhome="C:\Program Files (x8 ...

  4. 【DDD】领域驱动设计实践 —— Application层实现

    本文是DDD框架实现讲解的第二篇,主要介绍了DDD的Application层的实现,详细讲解了service.assemble的职责和实现.文末附有github地址.相比于<领域驱动设计> ...

  5. Check for Palindromes-FCC

    問題: 检查回文字符串 如果给定的字符串是回文,返回true,反之,返回false. 如果一个字符串忽略标点符号.大小写和空格,正着读和反着读一模一样,那么这个字符串就是palindrome(回文). ...

  6. java学习——java中的反射学习笔记

    Java--reflect 一.Class类的使用 什么是Class类? 1:在面向对象的世界中,万事万物皆对象. java语言中,静态的成员,普通数据类型类是不是对象呢? 是,对象!是类的对象! 类 ...

  7. CentOS 引导 Win10 启动项

    因为无聊,所以想尝试一下双系统,所以在win10的基础之上,装了一个Linux系统,之前装过Ubuntu,几乎都是自动完成的无任何压力.但是想着Ubuntu好像更新换代有点快,所以换了个能用比较久的C ...

  8. Reverse Words in a String III

    Given a string, you need to reverse the order of characters in each word within a sentence while sti ...

  9. POJ 2359 Questions(约瑟夫环——数学解法)

    题目链接: http://poj.org/problem?id=2359 题意描述: 输入一个字符串 按照下面的规则,如果剩下的最后一个字符是'?',输出"Yes",如果剩下的最后 ...

  10. Java公开课-01.类和对象

    一,类和对象的含义 1.类:类是具有相同属性(静态特征)和行为(功能 )的一系列事物的集合. eg:以下俩者是不是类 1)汽车  √ 2)小胖桌子上那个红色的杯子  × 2.对象:被精确限定到一个特殊 ...