微信公众平台开发 - 基础篇

微信公众平台 提供了很棒的 服务模式。使得用户和商家沟通更便捷,可以提供更好的会员服务。消息推送,优惠信息推送。它还提供了自定义菜单功能。从程序员视角我们看看如何做开发:

 

平台结构:

用户操作手机发送消息时:

用户APP(微信)   ->     微信公共平台     ->   我们开发的网站后台(基于微信公众平台REST API开发)

后台根据用户发送的内容,回复消息时:

用户APP(微信)   <-     微信公共平台     <-   我们开发的网站后台(基于微信公众平台REST API开发)

 

因为是基于微信开发的。我们需要做的是,建立一个网站,用于接收“ 微信公共平台发来的消息并回复”,而  微信公共平台 会将 “消息”发送到用户的手机。

如何做?  

准备:定义一个Servlet,其get方法处理“验证消息真实性”,POST方法处理“接收和回复消息”

第一步,验证消息真实性   参考:开发指南

需要处理GET消息,上代码:

微信公众平台开发 - 基础篇
/**

     * 验证 消息请求 是否 合法

     * @param request

     * @param response

     * @throws IOException

     */

    public void rerifyRequest(HttpServletRequest request, HttpServletResponse response)

            throws IOException {

        if(token == null)

            throw new IllegalArgumentException("未指定要验证的token");

        /**

         * 第二步:验证URL有效性

         * 

         * 开发者提交信息后,微信服务器将发送GET请求到填写的URL上,GET请求携带四个参数:

         * 

         * 参数 描述 signature

         * 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 timestamp

         * 时间戳 nonce 随机数 echostr 随机字符串

         * 开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器

         * ,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

         * 

         * 加密/校验流程如下: 1. 将token、timestamp、nonce三个参数进行字典序排序 2.

         * 将三个参数字符串拼接成一个字符串进行sha1加密 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

         * 检验signature的PHP示例代码:

         * 

         * 

         */

        Logger logger = Logger.getLogger("pdwy");

        logger.log(Level.INFO, "日志开始");



        String signature = request.getParameter("signature");

        String nonce = request.getParameter("nonce");

        String timestamp = request.getParameter("timestamp");

        String echostr = request.getParameter("echostr");



//        logger.log(Level.INFO, String.format(

//                "收到:signature=%s,nonce=%s,timestamp=%s,echostr=%s", signature,

//                nonce, timestamp, echostr));

        if (null == signature || "".equals(signature)) {

            response.sendError(500);

            return;

        }



        String[] arrs = new String[] { token, timestamp, nonce };

        Arrays.sort(arrs);

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < arrs.length; i++) {

            sb.append(arrs[i]);

        }

        String newSignature = sha1(sb.toString());

        if (signature.equals(newSignature)) {

            PrintWriter pw = response.getWriter();

            pw.write(echostr);

            pw.close();

        }

    }
View Code

 

第二步 ,接收消息和回复消息

需要处理post消息,上代码:

微信公众平台开发 - 基础篇
    /**

     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse

     *      response)

     */

    protected void doPost(HttpServletRequest request,

            HttpServletResponse response) throws ServletException, IOException {

        response.setCharacterEncoding("utf-8");

        request.setCharacterEncoding("utf-8");

        // 读消息

        String messageBody = readMessageBodyFromRequest(request);

        LogHelper.i( "\r\n收到POST的消息:" + messageBody);

        String replayMsgStr = handleMsg(messageBody);

        // 写入消息

        if (replayMsgStr != null)

            writeResponse(replayMsgStr, response);

        else

            writeResponse("", response);

    }
View Code

我们会收到一些xml格式的消息,接收到POST来的消息后,我们需要解析它,如何解析?下面是个文本消息的解析示例:

微信公众平台开发 - 基础篇
/**

 * 文本消息

 * 

 * ToUserName 开发者微信号 FromUserName 发送方帐号(一个OpenID) CreateTime 消息创建时间 (整型) MsgType

 * text Content 文本消息内容 MsgId 消息id,64位整型

 * 

 * @author yunfei

 * 

 */

public class TextMessage {

    /*

     * <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>

     */

    public String ToUserName;

    public String FromUserName;

    public String CreateTime;

    public String MsgType = "text";

    public String Content;

    public String MsgId;



    @Override

    public String toString() {

        StringBuilder sb = new StringBuilder();

        sb.append(String.format("%s=%s", "ToUserName", ToUserName));

        sb.append(String.format("%s=%s", "FromUserName", FromUserName));

        sb.append(String.format("%s=%s", "CreateTime", CreateTime));

        sb.append(String.format("%s=%s", "MsgType", MsgType));

        sb.append(String.format("%s=%s", "Content", Content));

        sb.append(String.format("%s=%s", "MsgId", MsgId));

        return sb.toString();

    }



    public static TextMessage fromXml(String xml){

        return TextMessageReader.readTextMessage(xml);

    }

}



class TextMessageReader {



    public static TextMessage readTextMessage(String xmlStr) {

        try {



            DocumentBuilderFactory factory = DocumentBuilderFactory

                    .newInstance();

            DocumentBuilder builder = factory.newDocumentBuilder();

            InputSource inputSource = new InputSource(new StringReader(

                    xmlStr));

            inputSource.setEncoding("utf-8");

            Document doc = builder.parse(inputSource);

            

            NodeList nl = doc.getElementsByTagName("xml");

            if (nl != null && nl.getLength() > 0) {

                Element rootElement = (Element) nl.item(0);

                String ToUserName = rootElement

                        .getElementsByTagName("ToUserName").item(0)

                        .getTextContent();

                String FromUserName = rootElement

                        .getElementsByTagName("FromUserName").item(0)

                        .getTextContent();

                String CreateTime = rootElement

                        .getElementsByTagName("CreateTime").item(0)

                        .getTextContent();

                String MsgType = rootElement.getElementsByTagName("MsgType")

                        .item(0).getTextContent();

                String Content = rootElement.getElementsByTagName("Content")

                        .item(0).getTextContent();

                String MsgId = rootElement.getElementsByTagName("MsgId")

                        .item(0).getTextContent();



                TextMessage bean;

                bean = new TextMessage();

                bean.ToUserName = ToUserName;

                bean.FromUserName = FromUserName;

                bean.CreateTime = CreateTime;

                bean.MsgType = MsgType;

                bean.Content = Content;

                bean.MsgId = MsgId;



                return bean;

            }

        } catch (Exception e) {

            e.printStackTrace();

            LogHelper.e( "Error:" + e.getMessage());

        }

        return null;

    }

}
View Code

好吧。我们完成了解析的步骤。我们将xml消息解析成了 实体,然后根据不同的消息,不同的消息内容做我们的业务处理,收到“你好“,我们回复个”你也好“等。示例:

微信公众平台开发 - 基础篇
package weixinmobile.services.handlers;



import weixinFundation.core.common.WeixinMessageHandler;

import weixinFundation.core.messages.MusicMessageReply;

import weixinFundation.core.messages.TextAndImageMessageReply;

import weixinFundation.core.messages.TextAndImageMessageReply.Article;

import weixinFundation.core.messages.TextMessage;

import weixinFundation.core.messages.TextMessageReply;

import weixinFundation.core.utils.DateUtil;

import weixinFundation.core.utils.LogHelper;

import weixinmobile.services.handlers.TalkManager.TaskResponse;



public class TextMessageHandler implements WeixinMessageHandler {



    @Override

    public boolean handleMsg(String messageType, String messageBody,

            WeixinMessageHandlerResult result) {

        // 判读消息类型,按类型读取消息

        if ("text".equals(messageType)) {

            TextMessage msg = TextMessage.fromXml(messageBody);

            String content = msg.Content;



            // 根据对白内容,返回对话

            TaskResponse response = new TaskResponse();

            if ( TalkManager.talk(content, response)) {

                String replyMsgStr = response.content;

                TextMessageReply replayMsg = null;

                replayMsg = TextMessageReply.createTextReplyMessage(msg.ToUserName,

                        msg.FromUserName, replyMsgStr);

                String replayMsgStr = replayMsg.toXml();

                result.result = replayMsgStr;

                return true;

            }

            //当用户的消息,没有对应的处理内容。默认处理。

            TextMessageReply replayMsg = null;

            replayMsg = TextMessageReply.createTextReplyMessage(msg.ToUserName,

                    msg.FromUserName, DEFALUT_MESSAGE);

            String replayMsgStr = replayMsg.toXml();

            result.result = replayMsgStr;

            return true;

            

        }

        return false;

    }



    public static final String DEFALUT_MESSAGE = "请根据提示选择你的操作:\r\n" +

            "1.图文消息示例。\r\n" +

            "2.公司名称。\r\n" +

            "3.联系方式。\r\n" +

            "4.回复文本消息带超链接的演示。\r\n" +

            "5.回复音乐消息示例。\r\n";



}





/**

 * 对话

 * 

 * @author yunfei

 * 

 */

public class TalkManager {



    /**

     * 处理回复

     * 

     * @param content

     * @return

     */

    public static boolean talk(String content, TaskResponse response) {

        if ("2".equals(content) || content.contains("公司")) {

            response.content = "北京-----发展有限公司  - 农业资源管理事业部";

            return true;

        } else if ("3".equals(content) || content.contains("电话")) {

            response.content = "我们的联系电话是:010-xxxxx";

            return true;

        }else if ("4".equals(content)) {

            response.content = "更多信息请查看:<a href='http://www.baidu.com'>详情</a>";

            return true;

        }

        

        return false;

        // return String.format("“%s”??? 没听清,你再说一遍。", content);

    }



    public static class TaskResponse {

        public String content;

    }

}
View Code

我们构建了回复消息,处理POST消息的最后一步是写入 ”响应该次POST的内容,就是我们回复的消息内容“,我们一般会构建以个 ”回复的消息实体“再将实体转成成 xml格式的字符串,写入到响应流(HttpResponse).示例:

微信公众平台开发 - 基础篇
/**

 * 回复文本消息

 * @author yunfei

 *

 */

public class TextMessageReply  implements IReplyMessage{

/*

 * <xml>

<ToUserName><![CDATA[toUser]]></ToUserName>

<FromUserName><![CDATA[fromUser]]></FromUserName>

<CreateTime>12345678</CreateTime>

<MsgType><![CDATA[text]]></MsgType>

<Content><![CDATA[你好]]></Content>

</xml>

 * */

    

    

    public String ToUserName;

    public String FromUserName;

    public String CreateTime;

    public String MsgType = "text";

    public String Content;



    public String toXml() {

        return TextMessageReplyWriter.toXml(this);

    }

    



---------------------------





class TextMessageReplyWriter {



    /**

     * <xml> <ToUserName><![CDATA[toUser]]></ToUserName>

     * <FromUserName><![CDATA[fromUser]]></FromUserName>

     * <CreateTime>12345678</CreateTime> <MsgType><![CDATA[text]]></MsgType>

     * <Content><![CDATA[你好]]></Content> </xml>

     * 

     * @param replayMsg

     * @return

     */

    public static String toXml(TextMessageReply replayMsg) {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

        DocumentBuilder dbuilder = null;

        try {

            dbuilder = dbf.newDocumentBuilder();

        } catch (Exception ex) {

            ex.printStackTrace();

        }

        Document doc = dbuilder.newDocument();

        Element root = doc.createElement("xml");

        doc.appendChild(root);



        Element e;



        e = doc.createElement("ToUserName");

        e.appendChild(doc.createCDATASection(replayMsg.ToUserName));

        root.appendChild(e);



        e = doc.createElement("FromUserName");

        e.appendChild(doc.createCDATASection(replayMsg.FromUserName));

        root.appendChild(e);



        e = doc.createElement("CreateTime");

        e.setTextContent(replayMsg.CreateTime);

        root.appendChild(e);



        e = doc.createElement("MsgType");

        e.appendChild(doc.createCDATASection(replayMsg.MsgType));

        root.appendChild(e);



        e = doc.createElement("Content");

        e.appendChild(doc.createCDATASection(replayMsg.Content));

        root.appendChild(e);



        StringWriter sw = new StringWriter();

        XmlWriteUtil.callDomWriter(doc, sw, "utf-8");

        String xmlRes = sw.getBuffer().toString();

        return xmlRes;

    }





}
View Code

将xml回复的内容写入到响应流,就完成了一次的消息响应。

 

不过,还有很多事情要做。消息的类型还有很多,还有事件的处理。我们需要根据不同的消息做不同的消息封装,对回复的各种类型的消息做封装。

我写了自己的类库:weinxinFundation,

  •   实现了基本的 接收消息,回复消息(图文消息,音乐消息等)
  •   基本的事件(关注,地理,扫描二维码等)的封装类库。
  •   支持可扩展的消息处理器,自定义处理消息的方式。消息的实现方法处理器逻辑和类库逻辑分离。便于二次开发。
  •   消息链表方式,一个消息不被处理就会沿这链表继续传递,直到被处理后结束。

  

提供使用 weinxinFundation 框架开发的演示源代码,在这里你也可以反编译拿到我weinxinFundation 源代码。

      点击下载 http://yunpan.cn/QaceVpHfIiInj 访问密码 16f1

暂不免费提供weinxinFundation 源代码的下载,需要请联系我[email protected]

 

 我的其他文章:

微信公众平台开发 - 基础篇 

微信公众平台开发 - 动手篇。使用weinxinFundation开始一个微信公众平台的开发

你可能感兴趣的:(微信公众平台)