• 微信公众号开发(一)——开发模式接入,消息的接收与响应


    微信公众号

    1

    想自己开一个公众号,先学习一下用 Java 进行微信公众号的开发。

    微信公众号的管理有开发模式和编辑模式两种,两者是互斥的。

    腾讯是这么讲的:

    编辑模式:编辑模式指所有的公众号运营者都可以通过简单的编辑,设置“按关键字回复”等功能。您可以设定常用的文字/语言/图片/录音作为回复消息,并制定自动回复的规则。当订阅用户的行为符合自动回复规则的时候,就会收到自动回复的消息。

    开发模式:开发模式是为开发者提供与用户进行消息交互的能力。对于成功接入消息接口的公众账号,当用户发消息给公众号,微信公众平台服务器会使用http请求对接入的网址进行消息推送,第三方服务器可通过响应包回复特定结构,从而达到回复消息的目的。

    所以接下来要讲的是如果使用微信的开发模式。

    2 准备工作

    微信的开发模式如果要调用他的接口需要微信认证300元。微信有公共平台测试号:

    开发 — 开发者工具 — 公众号平台测试账号

    地址映射工具

    微信的 URL 要求必须是: http:// 80端口,且必须是能够在公网访问的,本地的不行。所以这里用到一个映射工具叫做 ngrok 。下载之后使用,cmd

    ngrok http 8080

    这里写图片描述

    3 开发者模式接入

    准备完毕开始开发,包结构如下:

    包结构

    开发者模式接入

    通过 servlet 的 GET 请求进行校验

    校验参数

    微信需要对以上四个参数进行校验,将 timestamp,nonce,token 三者排序拼接成字符串,再进行 Sha1 加密之后,如果和 signature 相同则表示校验通过:

    public static boolean checkSignature(String signature,String timestamp,String nonce){
            //排序
            String[] arr = new String[]{token,timestamp,nonce};
            Arrays.sort(arr);
    
            //生成字符串
            StringBuffer content = new StringBuffer();
            for(int i = 0; i < arr.length; i++){
                content.append(arr[i]);
            }
    
            //sha1 加密 java实现消息摘要加密
            String temp = getSha1(content.toString());
    
            //和微信传递过来的参数进行校验
            return temp.equals(signature);
        }

    sha1加密

    public static String getSha1(String str) {
            if (str == null || str.length() == 0) {
                return null;
            }
            char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                    'a', 'b', 'c', 'd', 'e', 'f' };
    
            try {
                MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
                mdTemp.update(str.getBytes("UTF-8"));
    
                byte[] md = mdTemp.digest();
                int j = md.length;
                char buf[] = new char[j * 2];
                int k = 0;
                for (int i = 0; i < j; i++) {
                    byte byte0 = md[i];
                    buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                    buf[k++] = hexDigits[byte0 & 0xf];
                }
                return new String(buf);
            } catch (Exception e) {
                return null;
            }
        }

    进行校验的servlet

    protected 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();
            if(CheckUtil.checkSignature(signature, timestamp, nonce)){
                out.print(echostr);
            }
        }

    配置web.xml

    <servlet>
        <servlet-name>weixinServlet</servlet-name>
        <servlet-class>com.shuiyujie.servlet.WeiXinServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>weixinServlet</servlet-name>
        <url-pattern>/wx.do</url-pattern>
      </servlet-mapping>

    访问效果

    访问是500,说明已经通了。接下来用 ngrok 映射工具生成映射地址,到公网上去访问看看行不行,这个在前面讲过不做演示。

    在访问成功之后,对微信中的服务器配置进行填写提交。

    配置成功

    可以提交并且启用开发模式了,启用了开发模式则编辑模式下的配置全部失效。

    4 消息的接收与响应

    图片来自与网络
    图片来自于网络

    数据交互的过程如上图所示,通过 Post 请求进行。根据请求类型的不同,微信规定了不同的参数,并且通过 XML 的格式进行传递。通过查看开发文档,我们就可以了解所需要的参数。又由于是通过 XML 的格式传递的,为了便于后台程序的解析,所以将 XML 装换成集合类型。

    4.1 文本消息的 PO 类

    参数列表

    通过微信文档可以知道,文本消息传递的参数列表如上图所示,所以要新建一个 Po 类。

    public class TextMessage {
            private String ToUserName;
            private String FromUserName;
            private long CreateTime;
            private String MsgType;
            private String Content;
            private String MsgId;

    添加 get, set方法

    4.2 XML 和集合互相转换的工具类

    XML 转集合

    public static Map<String,String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException{
    
            Map<String,String> map = new HashMap<String,String>();
            SAXReader reader = new SAXReader();
    
            InputStream ins = request.getInputStream();
            Document doc = reader.read(ins);
    
            //获取根节点
            Element root = doc.getRootElement();
    
            List<Element> list = root.elements();
    
            for(Element e : list){
                map.put(e.getName(), e.getText());
            }
            ins.close();
    
            return map;
        }

    文本信息转 XML

    public static String textMessageToXml(TextMessage textMessage){
            XStream xstream = new XStream();
            xstream.alias("xml", textMessage.getClass());
            return xstream.toXML(textMessage);
        }

    servlet doPost 接收响应

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            try {
                Map<String,String> map = MessageUtil.xmlToMap(request);
                String fromUserName = map.get("FromUserName");
                String toUserName = map.get("ToUserName");
                String msgType = map.get("MsgType");
                String content = map.get("Content");
    
                String message = null;
                if("text".equals(msgType)){
                    TextMessage text = new TextMessage();
                    text.setFromUserName(toUserName); //原来的信息发送者,将变成信息接受者
                    text.setToUserName(fromUserName); //原理的接受者,变成发送者
                    text.setMsgType("text"); //表示消息的类型是text类型
                    text.setCreateTime(new Date().getTime());
                    text.setContent("您发送的信息是:" + content);
                    message = MessageUtil.textMessageToXml(text); //装换成 xml 格式发送给微信解析
    
                    System.out.println(message);
                }
                out.print(message);
            } catch (DocumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
        }

    接下来需要启动 tomcat 运行程序。

    效果图

    这样消息回复的效果已经完成了,最后来让他能够接收更多类型的消息。比如常见的一些:关注后的消息回复,取消关注之后的消息回复,还有关键字的消息回复

    5 更多接收消息

    由于会接收多种消息类型,修改 MessageUtil 消息工具类,为了方便把常用的消息类型都定义成常量,并且将需要回复的消息,例如关注之后回复的消息,关键词回复的消息都定义好

    定义常量

    public class MessageUtil {  
    
        public static final String MESSAGE_TEXT = "text";
        public static final String MESSAGE_NEWS = "news";
        public static final String MESSAGE_IMAGE = "image";
        public static final String MESSAGE_VOICE = "voice";
        public static final String MESSAGE_MUSIC = "music";
        public static final String MESSAGE_VIDEO = "video";
        public static final String MESSAGE_LINK = "link";
        public static final String MESSAGE_LOCATION = "location";
        public static final String MESSAGE_EVNET = "event";
        public static final String MESSAGE_SUBSCRIBE = "subscribe";
        public static final String MESSAGE_UNSUBSCRIBE = "unsubscribe";
        public static final String MESSAGE_CLICK = "CLICK";
        public static final String MESSAGE_VIEW = "VIEW";
        public static final String MESSAGE_SCANCODE= "scancode_push";
    /**
         * 拼接文本消息
         * @param toUserName
         * @param fromUserName
         * @param content
         * @return
         */
        public static String initText(String toUserName,String fromUserName,String content){
            TextMessage text = new TextMessage();
            text.setFromUserName(toUserName);
            text.setToUserName(fromUserName);
            text.setMsgType(MessageUtil.MESSAGE_TEXT);
            text.setCreateTime(new Date().getTime());
            text.setContent(content);
            return textMessageToXml(text);
        }
    
        /**
         * 关注回复
         * @return
         */
        public static String menuText(){
            StringBuffer sb = new StringBuffer();
            sb.append("欢迎关注公众号弄浪的鱼:
    
    ");
            sb.append("1 回复关键字1
    ");
            sb.append("2 回复关键字2
    ");
            sb.append("3 回复关键字3
    
    ");
            sb.append("本公众号还在开发中,没啥能看的,别玩坏了哈~~");
            return sb.toString();
        }

    servlet对接收到的消息进行判断并作出响应

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            try {
                Map<String, String> map = MessageUtil.xmlToMap(request);
                String fromUserName = map.get("FromUserName");
                String toUserName = map.get("ToUserName");
                String msgType = map.get("MsgType");
                String content = map.get("Content");
    
                String message = null;
    
                if (MessageUtil.MESSAGE_TEXT.equals(msgType)) {
                    if ("1".equals(content)) {
                        message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.firstMenu());
                    } else if ("2".equals(content)) {
                        message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.secondMenu());
                    } else if ("3".equals(content)) {
                        message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.threeMenu());
                    } else if ("?".equals(content) || "?".equals(content)) {
                        message = MessageUtil.menuText();
                    }
                } else if (MessageUtil.MESSAGE_EVNET.equals(msgType)) {// 事件推送
                    String eventType = map.get("Event"); // 事件分成多种,分别判断处理
                    if (MessageUtil.MESSAGE_SUBSCRIBE.equals(eventType)) { // 这里先写一个关注之后的事件
                        message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.menuText());
                    }
                }
    
                System.out.println(message);
    
                out.print(message);
            } catch (DocumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
        }

    成功

    以上,关注回复和关键词回复成功。

    最后再把微信程序部署到服务器上就可以正常访问了,下一篇就总结怎么部署到阿里云。

  • 相关阅读:
    Coursera self-driving2, State Estimation and Localization Week2, kalman filter 卡尔曼滤波
    Coursera Self-driving1, introduction
    Coursera, Big Data 5, Graph Analytics for Big Data, Week 5
    初创电商公司Drop的数据湖实践
    什么是LakeHouse?
    Apache Hudi 0.5.1版本重磅发布
    Apache Hudi使用问题汇总(一)
    ApacheHudi常见问题汇总
    写入Apache Hudi数据集
    Hudi基本概念
  • 原文地址:https://www.cnblogs.com/shuiyj/p/13185249.html
Copyright © 2020-2023  润新知