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

微信公众号开发(一)——开发模式接入,消息的接收与响应_第1张图片

1

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

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

腾讯是这么讲的:

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

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

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

2 准备工作

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

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

地址映射工具

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

ngrok http 8080

微信公众号开发(一)——开发模式接入,消息的接收与响应_第2张图片

3 开发者模式接入

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

微信公众号开发(一)——开发模式接入,消息的接收与响应_第3张图片

开发者模式接入

通过 servlet 的 GET 请求进行校验

微信公众号开发(一)——开发模式接入,消息的接收与响应_第4张图片

微信需要对以上四个参数进行校验,将 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>weixinServletservlet-name>
    <servlet-class>com.shuiyujie.servlet.WeiXinServletservlet-class>
  servlet>
  <servlet-mapping>
    <servlet-name>weixinServletservlet-name>
    <url-pattern>/wx.dourl-pattern>
  servlet-mapping>

微信公众号开发(一)——开发模式接入,消息的接收与响应_第5张图片

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

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

微信公众号开发(一)——开发模式接入,消息的接收与响应_第6张图片

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

4 消息的接收与响应

微信公众号开发(一)——开发模式接入,消息的接收与响应_第7张图片
图片来自于网络

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

4.1 文本消息的 PO 类

微信公众号开发(一)——开发模式接入,消息的接收与响应_第8张图片

通过微信文档可以知道,文本消息传递的参数列表如上图所示,所以要新建一个 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 xmlToMap(HttpServletRequest request) throws IOException, DocumentException{

        Map map = new HashMap();
        SAXReader reader = new SAXReader();

        InputStream ins = request.getInputStream();
        Document doc = reader.read(ins);

        //获取根节点
        Element root = doc.getRootElement();

        List 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 运行程序。

微信公众号开发(一)——开发模式接入,消息的接收与响应_第9张图片

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

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("欢迎关注公众号弄浪的鱼:\n\n");
        sb.append("1 回复关键字1\n");
        sb.append("2 回复关键字2\n");
        sb.append("3 回复关键字3\n\n");
        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 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();
        }

    }

微信公众号开发(一)——开发模式接入,消息的接收与响应_第10张图片

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

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

你可能感兴趣的:(微信公众号开发(一)——开发模式接入,消息的接收与响应)