记 WebApi 开发 微信公众号

第一次写文,主要用来记录一些之前遇到的问题和解决方法。有写的不好的地方 请(咬) 见(我) 谅(啊)

使用工具:VS2017

环境:.NET4.5     Web API      dapper

跟这个视频一步一步学习的微信公众号快速开发教程-学习视频教程-腾讯课堂(他用的java后台)

视频中的文章:

微信公众号开发教程(一) 验证接入 - 简书  (一) 验证接入

微信公众号开发教程(二)消息接收与响应处理 - 简书(二)消息接收与响应处理

微信公众号开发教程(三)事件推送及关键字回复 - 简书(三)事件推送及关键字回复

https://www.jianshu.com/p/85573685f17d(四)自定义菜单

微信公众号开发教程(五)发送模板消息 - 简书  (五)发送模板消息

微信公众号开发教程(六)获取微信用户信息-网页授权 - 简书   (六)获取微信用户信息-网页授权

微信公众号开发教程(七)JSSDK-监听分享朋友圈事件 - 简书  (七)JSSDK-监听分享朋友圈事件

开始:

第一步:创建Web Api。本来打算使用.net4.0 的 不知道什么原因 在web 项目中没有找到 webapi 选项,就改用了.NET4.5

第二步:如何接入,如何配置接口地址,如何做内网穿透等 请参考上面的文章连接 和视频资料,这里不做赘述;

           添加一个 ApiController。添加一个Get方法(用来验证接入接口是否可用)一个Post方法(用来处理业务)对外接口就这两个就够了。

         下载 token验证代码:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318479&token=&lang=zh_CN  根据这个文章中的提示 下载 加密解密的代码,验证接口是要用到

          上代码(直接上图片,代码不多手打一下吧,还能加深印象):

其中:Gloabls.Token 是你在 微信接口配置信息那填写的Token

第三步:处理 用户 提交的一些信息(使用Post提交)

      直接上代码:

/// <summary>
        /// 微信POST的消息,消息内容在content中
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public HttpResponseMessage Post()
        {
            var requestContent = Request.Content.ReadAsStreamAsync().Result;
          
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(requestContent);
            string msgTypeStr = xmlDoc.SelectSingleNode("xml/MsgType").InnerText;//消息类型
            string userName = xmlDoc.SelectSingleNode("xml/FromUserName").InnerText;//发送人
            string efhName = xmlDoc.SelectSingleNode("xml/ToUserName").InnerText;//接受人
            string createTime = xmlDoc.SelectSingleNode("xml/CreateTime").InnerText;//时间戳
            string MsgId = msgTypeStr == "event" ? "" : xmlDoc.SelectSingleNode("xml/MsgId").InnerText;//消息ID,事件中没有

            //验证消息是否重复
            if (Removal(msgTypeStr, MsgId, userName, createTime))
            {
                return null;
            }

            switch (msgTypeStr)
            {
                case "text":
                    {
                        //这里处理能识别的消息
                        var content = xmlDoc.SelectSingleNode("xml/Content").InnerText;

                        //根据不同消息查询相应的回复//这里是你自己的业务了,不要偷懒
                        dbo_message msg = bllMsg.GetMessage(content, userName);

                        switch (msg.Msg_type)
                        {
                            case "text":
                                return SendText(userName, efhName, msg.Msg_str);
                            case "news":
                                {
                                    //整理数据并返回
                                    List<Article> list = new List<Article>();
                                    list.Add(new Article()
                                    {
                                        Description = msg.Description,
                                        PicUrl = msg.PicUrl,
                                        Title = msg.Title,
                                        Url = msg.Url
                                    });
                                    return SendNews(userName, efhName, list);
                                }
                            default:
                                return SendText(userName, efhName, IncomprehensionText());
                        }
                    }
                case "image":
                    var imgId = xmlDoc.SelectSingleNode("xml/MediaId").InnerText;
                    return SendImage(userName, efhName, imgId);
                case "event":
                    //事件处理比较多
                    return EvnetHandle(xmlDoc, userName, efhName);
                default:
                    {
                        return SendText(userName, efhName, IncomprehensionText());
                    }
            }
        }

 /// <summary>
        /// 验证消息是否重复如果重复 返回true
        /// </summary>
        /// <param name="msgTypeStr">消息类型</param>
        /// <param name="MsgId">消息编号</param>
        /// <param name="userName">发送人Id</param>
        /// <param name="createTime">时间戳</param>
        /// <returns></returns>
        private static bool Removal(string msgTypeStr, string MsgId, string userName, string createTime)
        {
            if (string.IsNullOrWhiteSpace(MsgId))
            {
                MsgId = userName + createTime;
            }
            //删除3分钟之前的消息
            foreach (var item in _msgId.ToList())
            {
                if (item.Value < DateTime.Now.AddMinutes(-3))
                {
                    _msgId.Remove(item.Key);
                }
            }
            //验证消息是否存在
            if (_msgId.Keys.Contains(MsgId))
            {
                return true;
            }
            else
            {
                _msgId[MsgId] = DateTime.Now;
            }
            return false;
        }

        private static string IncomprehensionText()
        {
            return "我还没长大,不明白你在说什么?*✧⁺˚⁺ପ(๑・ω・)੭ु⁾⁾ 好好学习天天向上";
        }

/// <summary>
        /// 事件处理(关注,点击按钮等)
        /// </summary>
        /// <param name="xmlDoc"></param>
        /// <param name="userName"></param>
        /// <param name="efhName"></param>
        /// <returns></returns>
        private static HttpResponseMessage EvnetHandle(XmlDocument xmlDoc, string userName, string efhName)
        {
            var eve = xmlDoc.SelectSingleNode("xml/Event").InnerText;
            switch (eve)
            {
                case "subscribe":
                    return SendText(userName, efhName, "谢谢观看");
                case "CLICK":
                    //获取按钮事件的key值,用于处理相关业务
                    var eventKey = xmlDoc.SelectSingleNode("xml/EventKey").InnerText;
                    if (eventKey == "OTHER_CLICK")//这些是创建的按钮Key
                    {
                        return SendText(userName, efhName, "谢谢观看");
                    }
                    else if (eventKey == "BUY_COURSE")//这里也是按钮Key
                    {
                        return SendText(userName, efhName, "谢谢观看!自己要回复的 消息");
                    }
                    else
                    {
                        return SendText(userName, efhName, IncomprehensionText());
                    }
                    break;

                case "VIEW":
                    var viewKey = xmlDoc.SelectSingleNode("xml/EventKey").InnerText;
                    return null;
                    break;
                default:
                    return SendText(userName, efhName, IncomprehensionText());
                    break;
            }


        }

        /// <summary>
        /// 发送图片消息
        /// </summary>
        /// <param name="userName">用户</param>
        /// <param name="efhName">公众号</param>
        /// <param name="imgId">图片Id</param>
        /// <returns></returns>
        private static HttpResponseMessage SendImage(string userName, string efhName, string imgId)
        {
            string responseContent = XmlHelper.XmlSerialize<ImageMsg>(new ImageMsg
            {
                FromUserName = efhName,
                Image = new ImageInfo(imgId),
                ToUserName = userName
            });

            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(responseContent, Encoding.UTF8, "application/xml"),
            };
        }

        /// <summary>
        /// 发送文本消息
        /// </summary>
        /// <param name="userName">用户</param>
        /// <param name="efhName">公众号</param>
        /// <param name="msg">消息内容</param>
        /// <returns></returns>
        private static HttpResponseMessage SendText(string userName, string efhName, string msg)
        {
            string responseContent = XmlHelper.XmlSerialize<TxtMsg>(new TxtMsg
            {
                FromUserName = efhName,
                Content = msg,
                ToUserName = userName
            });
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(responseContent, Encoding.UTF8, "application/xml"),
            };
        }

        /// <summary>
        /// 发送图文信息
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="efhName"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        private static HttpResponseMessage SendNews(string userName, string efhName, List<Article> articles)
        {
            string responseContent = XmlHelper.XmlSerialize<News>(new News
            {
                FromUserName = efhName,
                ArticleCount = articles.Count,
                Articles = articles,
                ToUserName = userName
            });
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(responseContent, Encoding.UTF8, "application/xml"),
            };
        }

这个纯代码爽不爽,这里主要是处理一些 用户发送的文本消息,和功能按钮事件具体的也 需要你自己写了,这里主要整理一些 发送消息的方法,方便发送(文本,图片,图文)。

第四步:回复的消息处理。

  微信接受消息是以xml格式发送的 ,使用序列化的时候需要注意 ,正常序列化会出现 对应 类型的 namespace  微信识别不出来,所以需要去掉这些,并且 是以<xml></xml>标签包裹消息,所以实体类上需要用 [XmlRoot("xml")]标注:

又是图片(哈哈哈,主要是图片看着更清晰)。一定要有 无参的构造函数,否则序列化时会报错(别问我怎么知道的):

文本消息:

图片消息:

图文消息:

对了,还有xml序列化的处理:

到这里基本上就可以用了!!是不是也没多少东西。

为避免有些看文章 跳过的(我自己就是)特在这里做一些提醒: WXBizMsgCrypt.GenarateSinature 如果没有找到这个方法请到第二步 token验证 步骤的地址中 去下载 代码(有源码哦)

还有一些引用到的

微信功能按钮创建 都是通用的,可在文章最上面的连接中找到。

加粗备注:回复的文本消息有字数限制(具体多少官方没有给出,我也没有验证),如果无故提示服务器异常的 请考虑是不是字数过多。字数过多的使用 微信的"客服消息"接口实现(这里也有字数限制,但是可以分成多个消息回复)。

正常消息可以在回复前检查一下字数,如果过长 直接返回null。或请等待 的字样。然后启动线程 把正确的消息分批回复

附上一段我自己的代码(具体业务 自己再去整理把,没有相应的dbo_enroll_course;其中的Gloabls.AccessToken为access_token;Gloabls.appId 和Gloabls.appSecret 在微信公众号管理中找吧):

  /// <summary>
        /// 过长消息回复代码
        /// </summary>
        /// <param name="enrolls"></param>
        /// <param name="userName"></param>
        private static void MoreMsg(List<dbo_enroll_course> enrolls, string userName)
        {
            Thread thread = new Thread(() =>
            {
                if (string.IsNullOrWhiteSpace(Gloabls.AccessToken))
                {
                    Gloabls.AccessToken = dalToken.GetToken();
                }
                foreach (var item in enrolls)
                {
                again:
                    string msg = JsonConvert.SerializeObject(new
                    {
                        touser = userName,
                        msgtype = "text",
                        text = new { content = string.Format("姓名:{0}\r\n手机号:{1}\r\n购买时间:{2}\r\n购买课程:{3}\r\n教学方式:{4}\r\n套餐名称:{5}\r\n套餐信息:{6}\r\n当前状态:{7}\r\n\r\n\r\n", item.Enroll_Name, item.Enroll_Phone, item.Create_Time, item.Course_Name, item.Course_Qulity, item.Course_Group, item.Course_Group_Des, item.State == 9 ? "已为您安排好课程,请到“ClassIn”APP中查看" : "我们的业务人员正在积极为您安排课程,请耐心等待") }
                    });
                    string rest = HttpHelper.RequestData("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + Gloabls.AccessToken, msg);
                    dynamic dy = JsonConvert.DeserializeObject<dynamic>(rest);
                    if (dy.errcode == "40001")
                    {
                        string res = HttpHelper.HttpGet(string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", Gloabls.appId, Gloabls.appSecret), "");

                        dynamic getdy = JsonConvert.DeserializeObject<dynamic>(res);
                        try
                        {
                            //把获取的access_token保存到数据
                            dalToken.UpdateTocke(getdy.access_token.Value);
                            Gloabls.AccessToken = getdy.access_token.Value;
                            goto again;
                        }
                        catch (System.Exception)
                        {
                            break;
                        }
                    }
                }

            });
            thread.Start();
        }

结束!!!谢谢观看,有什么好的建议请留言。

新增:使用测试公众号 开发的时候会有一个小问题,图文消息有时候微信会在 后面加上 一个参数 subscene=131。会导致404错误,可以重新关注 进行测试,如果电脑版微信没有问题,就可以忽略这个东西。正式版的公众号还没有试,试过后在写上来

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值