说到普通消息,然后挥发给微信服务器

作者: 编程  发布:2019-09-24
先声明一下,这是一个maven工程
pom文件需要的依赖:

 1 <dependency> 2     <groupId>dom4j</groupId> 3     <artifactId>dom4j</artifactId> 4     <version>1.6</version> 5 </dependency> 6 <dependency> 7     <groupId>commons-io</groupId> 8     <artifactId>commons-io</artifactId> 9     <version>2.5</version>10 </dependency>11 <dependency>12     <groupId>com.thoughtworks.xstream</groupId>13     <artifactId>xstream</artifactId>14     <version>1.4.9</version>15 </dependency>16 <dependency>17     <groupId>javax.servlet</groupId>18     <artifactId>javax.servlet-api</artifactId>19     <version>3.1.0</version>20 </dependency>

用java开发微信公众号:接收和被动回复普通消息(三),java公众

上篇说完了如何接入微信公众号,本文说一下微信公众号的最基本功能:普通消息的接收和回复。说到普通消息,那么什么是微信公众号所定义的普通消息呢,微信开发者文档中提到的接收的普通消息包括如下几类:

1.文本消息
2.图片消息
3.语音消息
4.视频消息
5.小视频消息
6.地理位置消息
7.链接消息(被动回复的消息)

被动回复的普通消息包括:

1.回复文本消息
2.回复图片消息
3.回复语音消息
4.回复视频消息
5.回复音乐消息
6.回复图文消息

其实接收消息和被动回复消息这两个动作是不分家的,这本来就是一个交互场景,一般情况就是公众号通过分析接收到的消息,会给出对应的回复。当然也不能排除一些特殊业务了。

如何接收消息

要接收的这7中消息的xml格式这里就不列出了,请到官方文档查看,有具体的格式定义和属性说明。格式很简单,基本共有属性包括ToUserName、FromUserName、CreateTime、MsgType、MsgId,并且每种类型有自己特殊的属性。

看到这里,其实就很明白了,接收消息的过程其实就是获取post请求的这个xml,然后对这个xml进行分析的过程。post请求的入口还是之前提到的微信公众号接入的那个地址,整个公众号的所有请求都会走这个入口,只是接入时是get请求,其它情况下是post请求。处理xml这里用了dom4j,xml处理代码如下,在servlet的post方法中调用parseXml方法即可:

public static Map parseXml(HttpServletRequest request) throws Exception {
        // 将解析结果存储在HashMap中
        Map map = new HashMap();

        // 从request中取得输入流
        InputStream inputStream = request.getInputStream();
        /*
         * 读取request的body内容 此方法会导致流读取问题 Premature end of file. Nested exception:
   * Premature end of file String requestBody =
   * inputStream2String(inputStream); System.out.println(requestBody);
   */
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();

        // 遍历所有子节点
        for (Element e : elementList)
            map.put(e.getName(), e.getText());

        // 释放资源
        inputStream.close();
        inputStream = null;

        return map;
    }

    private static String inputStream2String(InputStream is) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int i = -1;
        while ((i = is.read()) != -1) {
            baos.write(i);
        }
        return baos.toString();
    }

如何被动回复消息

下面我基于这样一个逻辑来演示构造回复的消息,接收到文本消息"文本",回复文本消息;接收到“图片”,回复图片消息;接收到“语音”,回复语音消息;接收到“视频”,回复视频消息;接收到“音乐”,回复音乐消息;接收到“图文”,回复图文消息。

以回复文本消息作为说明:

<xml>
<ToUserName><![CDATA[发消息的人,即订阅者]]></ToUserName>
<FromUserName><![CDATA[微信公众号本身]]></FromUserName>
<CreateTime>消息创建时间(整形)</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[消息内容]]></Content>
</xml>

前两个属性可以从接收的消息中获取,接收的消息格式如下:

 <xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName> 
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a text]]></Content>
 <MsgId>1234567890123456</MsgId>
 </xml>

其中接收消息格式中的ToUserName便是回复消息的FromUserName,接收消息格式中的FromUserName便是回复消息的ToUserName。

CreateTime为消息发送的时间戳。MsgType为消息类型,文本为text。Content为消息内容。

具体每一种类型消息的回复,就是构造此种类型的xml格式内容,格式大同小异,只是音乐、视频、语音、图文格式相对于文本消息构造的xml内容稍微复杂一点。具体可参考官方文档。这里不做赘述,相信各位一看便明白。

同样,接收消息可以参看这里。

代码已更新到github

上篇说完了如何接入微信公众号,本文说一下微信公众号的最基本功能:...

微信公众平台开发教程(二) 基本原理及消息接口

一、简介

微信消息都是基于xml文件进行接收发送的

一、基本原理

在开始做之前,大家可能对这个很感兴趣,但是又比较茫然。是不是很复杂?很难学啊?

其实恰恰相反,很简单。为了打消大家的顾虑,先简单介绍了微信公众平台的基本原理。

微信服务器就相当于一个转发服务器,终端(手机、Pad等)发起请求至微信服务器,微信服务器,然后将请求转发给自定义服务(这就里就是我们的具体实现)。

服务处理完毕,然后挥发给微信服务器,微信服务器再将具体响应回复到终端。

通信协议为:HTTP

数据格式为:XML

具体的流程如下图所示:

9159.com 1

其实,我们需要做的事情,就是对HTTP请求,做出响应。

具体的请求内容,我们按照特定的XML格式去解析,处理完毕后,也要按照特定的XML格式返回。

我们只需要一个简单的实现HttpHandler即可。

当然,微信平台还能实现更加复杂的业务,比如微信可以作为内嵌的浏览器,我们可以通过微信的链接,打开htm界面,然后实现自己的逻辑。 

 

上一节Koa2微信公众号开发(一),我们搭建好了本地调试环境并且接入了微信公众测试号。这一节我们就来看看公众号的消息管理。并实现一个自动回复功能。

微信文本消息的参数:

二、消息接口(官方文档)

Github源码: github.com/ogilhinn/ko…

<xml>    <ToUserName><![CDATA[toUser]]></ToUserName>    <FromUserName><![CDATA[fromUser]]></FromUserName>    <CreateTime>1348831860</CreateTime>    <MsgType><![CDATA[text]]></MsgType>    <Content><![CDATA[this is a test]]></Content>    <MsgId>1234567890123456</MsgId></xml>        <!--参数              描述            ToUserName       开发者微信号            FromUserName     发送方帐号            CreateTime       消息创建时间             MsgType          text            Content          文本消息内容            MsgId            消息id,64位整型        -->

申请消息接口

点击申请,填写网址url和token,其中token可由开发者可以任意填写,用作生成签名。

9159.com 2

阅读建议:微信公众平台开发文档mp.weixin.qq.com/wiki

微信回复文本消息的参数

网址接入

公众平台用户提交信息后,微信服务器将发送GET请求到填写的URL上,并且带上四个参数:

参数 描述
signature 微信加密签名
timestamp 时间戳
nonce 随机数
echostr 随机字符串

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,否则接入失败。

signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。

加密/校验流程:
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

 

二、接收消息

<xml>    <ToUserName><![CDATA[toUser]]></ToUserName>    <FromUserName><![CDATA[fromUser]]></FromUserName>    <CreateTime>12345678</CreateTime>    <MsgType><![CDATA[text]]></MsgType>    <Content><![CDATA[你好]]></Content></xml><!--参数             是否必须        描述    ToUserName          是            接收方帐号(收到的OpenID)    FromUserName        是            开发者微信号    CreateTime          是            消息创建时间     MsgType             是            text    Content             是            回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示) -->

消息推送

当普通微信用户向公众账号发消息时,微信服务器将POST该消息到填写的URL上。结构如下:

当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。

我会在相应的代码上加上注释,其他对应图片消息参数,以及回复图片消息参数、图文消息参数等等 请参考微信开发文档。

文本消息

 <xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName> 
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a test]]></Content>
 <MsgId>1234567890123456</MsgId>
 </xml>
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType text
Content 文本消息内容
MsgId 消息id,64位整型

2.1 接收普通消息数据格式

附上微信公众号接口文档链接:点击这里

图片消息

 <xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[image]]></MsgType>
 <PicUrl><![CDATA[this is a url]]></PicUrl>
 <MsgId>1234567890123456</MsgId>
 </xml>
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType image
PicUrl 图片链接
MsgId 消息id,64位整型

XML的结构基本固定,不同的消息类型略有不同。

二话不说 先附上主代码:

地理位置消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<MsgType><![CDATA[location]]></MsgType>
<Location_X>23.134521</Location_X>
<Location_Y>113.358803</Location_Y>
<Scale>20</Scale>
<Label><![CDATA[位置信息]]></Label>
<MsgId>1234567890123456</MsgId>
</xml> 
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType location
Location_X 地理位置纬度
Location_Y 地理位置经度
Scale 地图缩放大小
Label 地理位置信息
MsgId 消息id,64位整型

用户发送文本消息时,微信公众账号接收到的XML数据格式如下所示:

注: xml解析:本文使用的是xtream进行xml与java对象之间的转换;对于xml解析还可以使用其他方法 如:dom4j等等 我这里就不一一列举了

链接消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<MsgType><![CDATA[link]]></MsgType>
<Title><![CDATA[公众平台官网链接]]></Title>
<Description><![CDATA[公众平台官网链接]]></Description>
<Url><![CDATA[url]]></Url>
<MsgId>1234567890123456</MsgId>
</xml> 
参数 描述
ToUserName 接收方微信号
FromUserName 发送方微信号,若为普通用户,则是一个OpenID
CreateTime 消息创建时间
MsgType 消息类型,link
Title 消息标题
Description 消息描述
Url 消息链接
MsgId 消息id,64位整型
<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>createTime</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a test]]></Content>
 <MsgId>1234567890123456</MsgId>
</xml>
import com.replymessage.*;import com.requestmessage.RequestTextMessage;import com.thoughtworks.xstream.XStream;import com.thoughtworks.xstream.io.xml.DomDriver;import com.util.Articles;import com.util.Item;import com.util.Music;import org.apache.commons.io.IOUtils;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.text.SimpleDateFormat;import java.util.Date;/** * 微信回复消息 */public class WbChat extends HttpServlet {    private static final long serialVersionUID = 1L;    public WbChat() {        super();    }    @Override    protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {        //这里没有对发送信息者进行验证,直接返回了,要加验证的话自己去百度吧        response.setContentType("text/html;charset=UTF-8");        PrintWriter writer = response.getWriter();        String echostr = request.getParameter("echostr");        echostr = new String(echostr.getBytes("ISO-8859-1"),"UTF-8");        writer.println;        //当你提交成为开发者的URL和Token时,微信服务器将发送GET请求到填写的URL上,        //只有你返回参数echostr时,他才会认为你这个接口是通的,才会提交成功    }    @Override    protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {        //当你用微信给平台发送信息时就会到这里        //回复音乐和图文消息,都写死了,自己可以根据自己的需要加相应的处理        response.setContentType("text/html;charset=UTF-8");        PrintWriter pw = response.getWriter();        String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8");        RequestTextMessage textMsg = null;        try {            textMsg = getRequestTextMessage;        } catch (Exception e) {            e.printStackTrace();        }        StringBuffer replyMsg = new StringBuffer();        String receive = textMsg.getContent;        String returnXml = null;        if (textMsg != null&&!receive.equals("")) {            if (receive.equals||receive.equals("?")) {                replyMsg.append("欢迎使用微信平台!");                replyMsg.append("rn1、当前时间");                replyMsg.append("rn2、听音乐");                replyMsg.append("rn3、看图文");                replyMsg.append("rn其他、请直接输入文字信息等待管理员回复");                returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName(),textMsg.getToUserName;            } else  if (receive.equals("2")) {                //回复音乐信息                returnXml = getReplyMusicMessage(textMsg.getFromUserName(),textMsg.getToUserName;            } else  if (receive.equals("3")) {                //回复图文                returnXml = getReplyTuwenMessage(textMsg.getFromUserName(),textMsg.getToUserName;            }  else if (receive.equals("1")) {                //回复时间                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");                replyMsg.append("当前时间rn"+df.format(new Date;                returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName(),textMsg.getToUserName;            } else {                replyMsg.append("收到: " + textMsg.getContent;                returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName(),textMsg.getToUserName;            }        } else {            replyMsg.append("别闹了,这谁看得懂啊!");            returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName(),textMsg.getToUserName;        }        pw.println(returnXml);    }    //获取推送文本消息    private RequestTextMessage getRequestTextMessage(String xml){        XStream xstream = new XStream(new DomDriver;        xstream.alias("xml", RequestTextMessage.class);        xstream.aliasField("ToUserName", RequestTextMessage.class, "toUserName");        xstream.aliasField("FromUserName", RequestTextMessage.class, "fromUserName");        xstream.aliasField("CreateTime", RequestTextMessage.class, "createTime");        xstream.aliasField("MsgType", RequestTextMessage.class, "messageType");        xstream.aliasField("Content", RequestTextMessage.class, "content");        xstream.aliasField("MsgId", RequestTextMessage.class, "msgId");        RequestTextMessage requestTextMessage = (RequestTextMessage)xstream.fromXML;        return requestTextMessage;    }    //回复文本消息    private String getReplyTextMessage(String content, String fromUserName,String toUserName){        ReplyTextMessage we = new ReplyTextMessage();        we.setMessageType("text");        we.setFuncFlag("0");        we.setCreateTime(new Long(new Date().getTime.toString;        we.setContent;        we.setToUserName(fromUserName);        we.setFromUserName(toUserName);        XStream xstream = new XStream(new DomDriver;        xstream.alias("xml", ReplyTextMessage.class);        xstream.aliasField("ToUserName", ReplyTextMessage.class, "toUserName");        xstream.aliasField("FromUserName", ReplyTextMessage.class, "fromUserName");        xstream.aliasField("CreateTime", ReplyTextMessage.class, "createTime");        xstream.aliasField("MsgType", ReplyTextMessage.class, "messageType");        xstream.aliasField("Content", ReplyTextMessage.class, "content");        xstream.aliasField("FuncFlag", ReplyTextMessage.class, "funcFlag");        String xml =xstream.toXML;        return xml;    }    //回复音乐消息    private String getReplyMusicMessage(String fromUserName,String toUserName){        ReplyMusicMessage we = new ReplyMusicMessage();        Music music = new Music();        we.setMessageType("music");        we.setCreateTime(new Long(new Date().getTime.toString;        we.setToUserName(fromUserName);        we.setFromUserName(toUserName);        we.setFuncFlag("0");        music.setTitle("home");        music.setDescription(" family 想家了吗");        String url = "http://bcs.duapp.com/yishi-music/%E5%9B%9E%E5%AE%B6.mp3?sign=MBO:97068c69ccb2ab230a497c59d528dcce:LdYZ%2FLXohKa6YCy9gbxL%2B1mZ4Co%3D";        String url2 = "http://bcs.duapp.com/yishi-music/X-man.mp3?sign=MBO:97068c69ccb2ab230a497c59d528dcce:cYV%2B%2Fq2Tlv2de6gqecZynCyIm3k%3D";        music.setMusicUrl;        music.setHqMusicUrl;        we.setMusic;        XStream xstream = new XStream(new DomDriver;        xstream.alias("xml", ReplyMusicMessage.class);        xstream.aliasField("ToUserName", ReplyMusicMessage.class, "toUserName");        xstream.aliasField("FromUserName", ReplyMusicMessage.class, "fromUserName");        xstream.aliasField("CreateTime", ReplyMusicMessage.class, "createTime");        xstream.aliasField("MsgType", ReplyMusicMessage.class, "messageType");        xstream.aliasField("FuncFlag", ReplyMusicMessage.class, "funcFlag");        xstream.aliasField("Music", ReplyMusicMessage.class, "Music");        xstream.aliasField("Title", Music.class, "title");        xstream.aliasField("Description", Music.class, "description");        xstream.aliasField("MusicUrl", Music.class, "musicUrl");        xstream.aliasField("HQMusicUrl", Music.class, "hqMusicUrl");        String xml =xstream.toXML;        return xml;    }    //回复图文消息    private String getReplyTuwenMessage(String fromUserName,String toUserName){        ReplyTuwenMessage we = new ReplyTuwenMessage();        Articles articles = new Articles();        Item item = new Item();        we.setMessageType("news");        we.setCreateTime(new Long(new Date().getTime.toString;        we.setToUserName(fromUserName);        we.setFromUserName(toUserName);        we.setFuncFlag("0");        we.setArticleCount(1);        item.setTitle("二哈");        item.setDescription("二哈是Twitter上现在最流行的偶像犬,是一条傻狗,因为在网上装疯扮傻而走红网络。");        item.setPicUrl("http://bcs.duapp.com/yishi-music/111.jpg?sign=MBO:97068c69ccb2ab230a497c59d528dcce:hmzcBYxgI4yUaTd9GvahO1GvE%2BA%3D");        item.setUrl("http://baike.baidu.com/view/6300265.htm");        articles.setItem;        we.setArticles;        XStream xstream = new XStream(new DomDriver;        xstream.alias("xml", ReplyTuwenMessage.class);        xstream.aliasField("ToUserName", ReplyTuwenMessage.class, "toUserName");        xstream.aliasField("FromUserName", ReplyTuwenMessage.class, "fromUserName");        xstream.aliasField("CreateTime", ReplyTuwenMessage.class, "createTime");        xstream.aliasField("MsgType", ReplyTuwenMessage.class, "messageType");        xstream.aliasField("Articles", ReplyTuwenMessage.class, "Articles");        xstream.aliasField("ArticleCount", ReplyTuwenMessage.class, "articleCount");        xstream.aliasField("FuncFlag", ReplyTuwenMessage.class, "funcFlag");        xstream.aliasField("item", Articles.class, "item");        xstream.aliasField("Title", Item.class, "title");        xstream.aliasField("Description", Item.class, "description");        xstream.aliasField("PicUrl", Item.class, "picUrl");        xstream.aliasField("Url", Item.class, "url");        String xml =xstream.toXML;        return xml;    }}

事件推送

事件推送只支持微信4.5版本,目前开启自定义菜单接口事件推送、关注与取消关注事件推送。其余功能即将开放,敬请期待。

<xml><ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[EVENT]]></Event>
<EventKey><![CDATA[EVENTKEY]]></EventKey>
</xml>
参数 描述
ToUserName 接收方微信号
FromUserName 发送方微信号,若为普通用户,则是一个OpenID
CreateTime 消息创建时间
MsgType 消息类型,event
Event 事件类型,subscribe(订阅)、unsubscribe(取消订阅)、CLICK(自定义菜单点击事件)
EventKey 事件KEY值,与自定义菜单接口中KEY值对应

用户发送图片消息时,微信公众账号接收到的XML数据格式如下所示:

当你看到这里 说明你成功一半了,下面我们继续来完成这些小瑕疵

消息回复

对于每一个POST请求,开发者在响应包中返回特定xml结构,对该消息进行响应(现支持回复文本、图文、语音、视频、音乐)。

微信服务器在五秒内收不到响应会断掉连接。

回复xml结构如下:

<xml> 
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>1348831860</CreateTime> 
 <MsgType><![CDATA[image]]></MsgType> 
 <PicUrl><![CDATA[this is a url]]></PicUrl>
 <MediaId><![CDATA[media_id]]></MediaId> 
 <MsgId>1234567890123456</MsgId>
</xml>

这时候需要:根据xml标签创建对应的实体类

回复文本消息

 <xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>12345678</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[content]]></Content>
 </xml>
参数 描述
ToUserName 接收方帐号(收到的OpenID)
FromUserName 开发者微信号
CreateTime 消息创建时间
MsgType text
Content 回复的消息内容,长度不超过2048字节

其他消息消息类型的结构请查阅【微信公众平台开发文档】

实体类一:音乐消息

回复音乐消息

 <xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>12345678</CreateTime>
 <MsgType><![CDATA[music]]></MsgType>
 <Music>
 <Title><![CDATA[TITLE]]></Title>
 <Description><![CDATA[DESCRIPTION]]></Description>
 <MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
 <HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
 </Music>
 </xml>
参数 描述
ToUserName 接收方帐号(收到的OpenID)
FromUserName 开发者微信号
CreateTime 消息创建时间
MsgType music
MusicUrl 音乐链接
HQMusicUrl 高质量音乐链接,WIFI环境优先使用该链接播放音乐

对于POST请求的处理,koa2没有封装获取参数的方法,需要通过自己解析上下文context中的原生node.js请求对象request。我们将用到row-body这个模块来拿到数据。

import com.util.Music;/** * 音乐消息实体类 */public class ReplyMusicMessage {    private String toUserName;    private String fromUserName;    private String createTime;    private String messageType;    private com.util.Music Music;    private String funcFlag;//这里的funcFlag,微信平台接口文档里没有,但是必须写上,不然会收不到返回的信息    public String getToUserName() {        return toUserName;    }    public void setToUserName(String toUserName) {        this.toUserName = toUserName;    }    public String getFromUserName() {        return fromUserName;    }    public void setFromUserName(String fromUserName) {        this.fromUserName = fromUserName;    }    public String getCreateTime() {        return createTime;    }    public void setCreateTime(String createTime) {        this.createTime = createTime;    }    public String getMessageType() {        return messageType;    }    public void setMessageType(String messageType) {        this.messageType = messageType;    }    public Music getMusic() {        return Music;    }    public void setMusic(Music music) {        Music = music;    }    public String getFuncFlag() {        return funcFlag;    }    public void setFuncFlag(String funcFlag) {        this.funcFlag = funcFlag;    }}

回复图文消息

 <xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>12345678</CreateTime>
 <MsgType><![CDATA[news]]></MsgType>
 <ArticleCount>2</ArticleCount>
 <Articles>
 <item>
 <Title><![CDATA[title1]]></Title> 
 <Description><![CDATA[description1]]></Description>
 <PicUrl><![CDATA[picurl]]></PicUrl>
 <Url><![CDATA[url]]></Url>
 </item>
 <item>
 <Title><![CDATA[title]]></Title>
 <Description><![CDATA[description]]></Description>
 <PicUrl><![CDATA[picurl]]></PicUrl>
 <Url><![CDATA[url]]></Url>
 </item>
 </Articles>
 </xml> 
参数 描述
ToUserName 接收方帐号(收到的OpenID)
FromUserName 开发者微信号
CreateTime 消息创建时间
MsgType news
ArticleCount 图文消息个数,限制为10条以内
Articles 多条图文消息信息,默认第一个item为大图
Title 图文消息标题
Description 图文消息描述
PicUrl 图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80。
Url 点击图文消息跳转链接

2.2 先来优化之前的代码

实体类二:文本消息

官方接口文档:

这一节的代码紧接着上一届实现的代码,在上一届的基础上轻微改动了下。

/** * 文本消息实体类 */public class ReplyTextMessage {    private String toUserName;    private String fromUserName;    private String createTime;    private String messageType;    private String content;    private String funcFlag;    public String getToUserName() {        return toUserName;    }    public void setToUserName(String toUserName) {        this.toUserName = toUserName;    }    public String getFromUserName() {        return fromUserName;    }    public void setFromUserName(String fromUserName) {        this.fromUserName = fromUserName;    }    public String getCreateTime() {        return createTime;    }    public void setCreateTime(String createTime) {        this.createTime = createTime;    }    public String getMessageType() {        return messageType;    }    public void setMessageType(String messageType) {        this.messageType = messageType;    }    public String getContent() {        return content;    }    public void setContent(String content) {        this.content = content;    }    public String getFuncFlag() {        return funcFlag;    }    public void setFuncFlag(String funcFlag) {        this.funcFlag = funcFlag;    }}

注意事项

1.用户OpenID对一个公众号是固定唯一的串

2.请使用80端口

'use strict'

const Koa = require('koa')
const app = new Koa()
const crypto = require('crypto')
// 将配置文件独立到config.js
const config = require('./config')

app.use(async ctx => {
 // GET 验证服务器
 if (ctx.method === 'GET') {
  const { signature, timestamp, nonce, echostr } = ctx.query
  const TOKEN = config.wechat.token
  let hash = crypto.createHash('sha1')
  const arr = [TOKEN, timestamp, nonce].sort()
  hash.update(arr.join(''))
  const shasum = hash.digest('hex')
  if (shasum === signature) {
   return ctx.body = echostr
  }
  ctx.status = 401
  ctx.body = 'Invalid signature'
 } else if (ctx.method === 'POST') { // POST接收数据
  // TODO
 }
});

app.listen(7001);

实体类三:图文消息

尽请关注:后续我们将全面讲解具体的开发过程。

这儿我们在只在GET中验证了签名值是否合法,实际上我们在POST中也应该验证签名。

import com.util.Articles;/** * 图文消息实体类 */public class ReplyTuwenMessage {    private String toUserName;    private String fromUserName;    private String createTime;    private String messageType;    private int articleCount;    private Articles articles;    private String funcFlag;    public String getToUserName() {        return toUserName;    }    public void setToUserName(String toUserName) {        this.toUserName = toUserName;    }    public String getFromUserName() {        return fromUserName;    }    public void setFromUserName(String fromUserName) {        this.fromUserName = fromUserName;    }    public String getCreateTime() {        return createTime;    }    public void setCreateTime(String createTime) {        this.createTime = createTime;    }    public String getMessageType() {        return messageType;    }    public void setMessageType(String messageType) {        this.messageType = messageType;    }    public int getArticleCount() {        return articleCount;    }    public void setArticleCount(int articleCount) {        this.articleCount = articleCount;    }    public Articles getArticles() {        return articles;    }    public void setArticles(Articles articles) {        this.articles = articles;    }    public String getFuncFlag() {        return funcFlag;    }    public void setFuncFlag(String funcFlag) {        this.funcFlag = funcFlag;    }}

三、消息类图

 9159.com 3

将签名验证写成一个函数

请求消息实体类

function getSignature (timestamp, nonce, token) {
 let hash = crypto.createHash('sha1')
 const arr = [token, timestamp, nonce].sort()
 hash.update(arr.join(''))
 return hash.digest('hex')
}
/** * 请求消息实体类 */public class RequestTextMessage {    private String toUserName;    private String fromUserName;    private String createTime;    private String messageType;    private String content;    private String msgId;//getters,setters    public String getToUserName() {        return toUserName;    }    public void setToUserName(String toUserName) {        this.toUserName = toUserName;    }    public String getFromUserName() {        return fromUserName;    }    public void setFromUserName(String fromUserName) {        this.fromUserName = fromUserName;    }    public String getCreateTime() {        return createTime;    }    public void setCreateTime(String createTime) {        this.createTime = createTime;    }    public String getMessageType() {        return messageType;    }    public void setMessageType(String messageType) {        this.messageType = messageType;    }    public String getContent() {        return content;    }    public void setContent(String content) {        this.content = content;    }    public String getMsgId() {        return msgId;    }    public void setMsgId(String msgId) {        this.msgId = msgId;    }}

优化代码,再POST中也加入验证

音乐消息实体类

...

app.use(async ctx => {
 const { signature, timestamp, nonce, echostr } = ctx.query
 const TOKEN = config.wechat.token
 if (ctx.method === 'GET') {
  if (signature === getSignature(timestamp, nonce, TOKEN)) {
   return ctx.body = echostr
  }
  ctx.status = 401
  ctx.body = 'Invalid signature'
 }else if (ctx.method === 'POST') {
  if (signature !== getSignature(timestamp, nonce, TOKEN)) {
   ctx.status = 401
   return ctx.body = 'Invalid signature'
  }
  // TODO
 }
});
...
/** * 音乐消息实体类 */public class Music {    private String title;    private String description;    private String musicUrl;    private String hqMusicUrl;//getters,setters    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    public String getDescription() {        return description;    }    public void setDescription(String description) {        this.description = description;    }    public String getMusicUrl() {        return musicUrl;    }    public void setMusicUrl(String musicUrl) {        this.musicUrl = musicUrl;    }    public String getHqMusicUrl() {        return hqMusicUrl;    }    public void setHqMusicUrl(String hqMusicUrl) {        this.hqMusicUrl = hqMusicUrl;    }}

到这儿我们都没有开始实现接受XML数据包的功能,而是在修改之前的代码。这是为了演示在实际开发中的过程,写任何代码都不是一步到位的,好的代码都是改出来的。

xml嵌套标签实体类

2.3 接收公众号普通消息的XML数据包

/** * xml嵌套标签实体类 */public class Articles {    private Item item;    public Item getItem() {        return item;    }    public void setItem(Item item) {        this.item = item;    }    }

现在开始进入本节的重点,接受XML数据包并转为JSON

xml嵌套标签实体类

$ npm install raw-body --save


...
const getRawBody = require('raw-body')
...

// TODO
// 取原始数据
const xml = await getRawBody(ctx.req, {
 length: ctx.request.length,
 limit: '1mb',
 encoding: ctx.request.charset || 'utf-8'
});
console.log(xml)
return ctx.body = 'success' // 直接回复success,微信服务器不会对此作任何处理
/** * xml嵌套标签实体类 */public class Item {    private String title;    private String description;    private String picUrl;    private String url;    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    public String getDescription() {        return description;    }    public void setDescription(String description) {        this.description = description;    }    public String getPicUrl() {        return picUrl;    }    public void setPicUrl(String picUrl) {        this.picUrl = picUrl;    }    public String getUrl() {        return url;    }    public void setUrl(String url) {        this.url = url;    }}

给你的测试号发送文本消息,你可以在命令行看见打印出如下数据

当你看到这里 恭喜你 微信公众号自动回复的基础操作你已经学会了

<xml>
 <ToUserName><![CDATA[gh_9d2d49e7e006]]></ToUserName>
 <FromUserName><![CDATA[oBp2T0wK8lM4vIkmMTJfFpk6Owlo]]></FromUserName>
 <CreateTime>1516940059</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[JavaScript之禅]]></Content>
 <MsgId>6515207943908059832</MsgId>
</xml>

后续还会有 在公众号基础上进行小活动开发 后续更新...

恭喜,到此你已经可以接收到XML数据了。

本文由9159.com发布于编程,转载请注明出处:说到普通消息,然后挥发给微信服务器

关键词:

上一篇:没有了
下一篇:没有了