• 微信公众平台开发


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

    平台结构:

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

    用户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( "
    收到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 = "请根据提示选择你的操作:
    " +
                "1.图文消息示例。
    " +
                "2.公司名称。
    " +
                "3.联系方式。
    " +
                "4.回复文本消息带超链接的演示。
    " +
                "5.回复音乐消息示例。
    ";
    
    }
    
    
    /**
     * 对话
     * 
     * @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 源代码的下载,需要请联系我vir56k@163.com

     我的其他文章:

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

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

  • 相关阅读:
    使用Razor视图引擎来生成邮件内容
    Asp .Net Core 2.0 登录授权以及多用户登录
    简单几步,提升.Net Core的开发效率
    百万数据测试 Entity Framework 到底有多慢
    纸壳CMS(ZKEACMS)体验升级,快速创建页面,直接在页面中修改内容
    ZKEACMS 配置使用 HTTPS
    使用 jQuery.Pin 垂直滚动时固定导航
    底层的增删查改
    关于hangfire的使用
    巧用 CSS 实现酷炫的充电动画
  • 原文地址:https://www.cnblogs.com/vir56k/p/3664181.html
Copyright © 2020-2023  润新知