• 上篇文章主要说了接口测试号的接入和配置,其实真正的服务号的接入和接口测试号的接入没什么问题,只不过可以选择加密传输而已。
  • 这篇主要记录如何接收用户发送的消息,以及被动的回复。
  • 值得注意的是,公众号不能主动的向用户推送消息,估计也是为了遵守一天一条订阅号全体推送吧

一、文档地址:

如果需要查找不同的消息类型的内容,需要查看微信的文档。

  1. 接收消息: http://mp.weixin.qq.com/wiki/17/f298879f8fb29ab98b2f2971d42552fd.html
  2. 回复消息: http://mp.weixin.qq.com/wiki/1/6239b44c206cab9145b1d52c67e6c551.html

二、前提

接收和发送消息的前提是必须配置了URL,也就是上篇文章提到的message.php

填写配置信息的时候填写的url和token如下:

  1. URL: http://www.ptbird.cn/weixin/develop/message.php?appid=wxxxxxxxxxx
  2. TOKEN: postbird

三、接收消息

1、消息的类型

用户发送给公众号的消息类型如下:

  1. 文本消息
  2. 图片消息
  3. 语音消息
  4. 视频消息
  5. 链接消息
  6. 小视频消息
  7. 地理位置消息

每种消息的内容是不一样的,通过xml来传输,但是一些固定的部分是不变的。

2、消息中的通用部分

使用文本消息举例

比如文本消息如下:

<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(openid是每个用户对该公众号的唯一标示)
  • msgtype 表示类型 text就表示 是文本类型

这些通用的部分,我们利用type判断消息类型,将发送者和接收者调换一下就可以进行消息的回复。

3、获取用户的消息

用户的消息就是从上面的xml中拿出来的。

  • text 类型最简单,Content中就是消息的内容
  • image 类型有PicUrl可以获得图片在微信上的地址,而MediaId可以获得该图片的id,公众号向用户发送消息的时候,要使用mediaId而不是PicUrl
  • 语音、视频、小视频和图片差不多,根据文档具体内容获取即可,不过没有url,只有mediaId

四、被动回复消息

这里的消息回复是被动的回复,也就是接收到了用户的消息,根据用户的消息作出反应。

就像是微信公众号管理平台设置的关键词回应差不多的意思。

微信的消息回复还是通过xml来响应消息的,因此我们需要根据不同的消息类型构建自己的消息的xml。

怎么构建看微信文档给出的格式就行了,这个很简单。

1、构建消息的XML


<xml>
        <ToUserName><![CDATA[toUser]]></ToUserName>
        <FromUserName><![CDATA[fromUser]]></FromUserName>
        <CreateTime>12345678</CreateTime>
        <MsgType><![CDATA[text]]></MsgType>
        <Content><![CDATA[你好]]></Content>
     </xml>

要点如下:

  1. ToUserName 对应接收时候的 FromUserName,就是用户openid
  2. FromUserName 代表本公众号,也就是上面接收到的 ToUserName
  3. CreateTime 是发送时间的时间戳,这个生成就OK
  4. MsgType 是消息的类型 根据自己要回复的消息
  5. Content就是文本消息的内容,其他的消息都是通过Media发送过去的(详细见微信文档)

2、XML输出即可发送

构建好正确的格式的XML,输出就可以,作为对微信的响应。

四、写代码

message.php上写代码的时候,将之前的signature的校验函数的调用注释掉就行了。

还是一样的道理,在这个上直接调用相关的函数或者是封装的类就行。

以函数举例(简单写的):


/**
 * 实现消息的交互
 */
function responseMsg(){
    //HTTP_RAW_POST_DATA 可以用来接收php无法识别的数据
    //比如text/xml 或者soap等 因此这里不能使用$_POST来接受微信的数据
    $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
    //如果不为空,则进行数据的处理
    if (!empty($postStr)) {
        //将xml解析成对象
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        //获取发送端对象的值
            //用于构建下面的发送方和接收方
            $fromUsername = $postObj->FromUserName;
            $mType = $postObj->MsgType;//消息类型
            $toUsername = $postObj->ToUserName;
        $time = time();
        $textTpl = "<xml>
                <ToUserName><![CDATA[%s]]></ToUserName>
                <FromUserName><![CDATA[%s]]></FromUserName>
                <CreateTime>%s</CreateTime>
                <MsgType><![CDATA[%s]]></MsgType>
                <Content><![CDATA[%s]]></Content>
                <FuncFlag>0</FuncFlag>
                </xml>";
        //回复内容
        $responseMsg = "";

        //判断消息内容 text image voice(语音) amr(语音识别) video(视频) shortvideo(小视频) location(地理位置) link(链接)
        if($mType!='text'){
            $responseMsg="只能发送文字消息";
        }
        switch($mType){
            case "text":
                //获取文本消息
                $receiveMsg = trim($postObj->Content);//内容
                //授权链接
                $responseMsg ='登录';
                $msgType = "text";
                //将变量写入模板中
                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $responseMsg);
                break;

            case "image":
                //回复图文消息
                $textTpl="
                    <![CDATA[%s]]>
                    <![CDATA[%s]]>
                    %s
                    <![CDATA[%s]]>
                    1
                    
                        
                            <![CDATA[%s]]> 
                            <![CDATA[%s]]>
                            <![CDATA[%s]]>
                            <![CDATA[%s]]>
                        
                    
                    ";
                $msgType="news";
                //获取链接地址
                $picUrl =$postObj->PicUrl;
                //标题
                $title="您发送了一张图片给我";
                //描述
                $description="点击链接可以直接查看图片";
                //将变量写入模板中
                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $title,$description,$picUrl,$picUrl);
                break;
            case "voice":
                $receiveMsg=$postObj->MediaId;
                $responseMsg = $receiveMsg;
                $msgType = "voice";
                //回复消息的模板
                $textTpl = "
                    <![CDATA[%s]]>
                    <![CDATA[%s]]>
                    %s
                    <![CDATA[%s]]>
                    
                    <![CDATA[%s]]>
                    
                    ";
                //将变量写入模板中
                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $responseMsg);
                break;
            case "video":
                $receiveMsg=$postObj->ThumbMediaId;
                $responseMsg = $receiveMsg;
                $msgType = "text";
                //将变量写入模板中
                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $responseMsg);
                break;
            default:
                $msgType="text";
                $responseMsg="未能识别的消息类型,只能发送图片和文字";
                //将变量写入模板中
                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $responseMsg);
                break;
        }
        //输出消息
        echo $resultStr;
    }
}

五、注意的问题

1、接收消息不是 post消息,因此不能使用post接收

微信发来的是 text/xml 的消息类型,因此消息接收的时候用的是:

 $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

2、接收的是xml而不是json,需要对xml正确解析和获取

解析方法很多,php的解析方法我用的是:

 //将xml解析成对象
    $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);

这样就可以通过下面的方法获取需要的内容了

 //用于构建下面的发送方和接收方
    $fromUsername = (string) $postObj->FromUserName;
    $mType = (string) $postObj->MsgType;//消息类型
    $toUsername = (string) $postObj->ToUserName;

需要注意的是,必须将其转换成string格式,强制类型转换,否则获取的是object的格式

3、错误

如果自己的代码出现错误,而无法回复,公众号会给用户发送提示,该公众号暂时无法提供服务,如果这样出现了,很可能是自己的代码出现了问题。

4、调试

除了在微信在线接口调试工具调试外,我们在调试代码的时候,想知道哪里的代码除了问题,平时用的最多的是截断输出,但是因为我们无法截断输出,所以我自己的解决办法如下:

  • 连接一个数据库,创建一个表,两个字段 str 和 time。

  • 在需要调试的地方将内容写入数据表中,增加记录,和截断输出没什么区别。

我自己觉得还不错,我一直都这样子调试,可以明确知道自己的代码哪里有问题,而哪里没有问题

六、效果

下面是我给自己发的图片,并返回一个图文消息:

postbird-weixin