zoukankan      html  css  js  c++  java
  • 使用web api开发微信公众号,调用图灵机器人接口(一)

    此文将分两篇讲解,主要分为以下几步

    1. 签名校验;
    2. 首次提交验证申请;
    3. 接收消息;
    4. 被动响应消息(返回XML);
    5. 映射图灵消息及微信消息;

    其实图灵机器人搭载微信公众号很简单,只需要把图灵的地址配到公众后台就可以了。
    不过这样做之后也就没有任何扩展的可能了,因此自己实现一套!

    一、签名校验

    在开发者首次提交验证申请时,微信服务器将发送GET请求到填写的URL上,并且带上四个参数(signature、timestamp、nonce、echostr),开发者通过对签名(即signature)的效验,来判断此条消息的真实性。

    此后,每次开发者接收用户消息的时候,微信也都会带上前面三个参数(signature、timestamp、nonce)访问开发者设置的URL,开发者依然通过对签名的效验判断此条消息的真实性。效验方式与首次提交验证申请一致。

    根据微信开发者平台中的描述,我们在首次提交验证申请及接收用户消息时,都需要校验签名以确保消息来源真实。

    参与签名的参数为timestampnoncetoken(即开发者中心中配置的Token令牌)

    加密/校验流程如下:

    1. 将token、timestamp、nonce三个参数进行字典序排序(此处注意:是三个参数的值,而不是按参数名排序)
    2. 将三个参数字符串拼接成一个字符串进行sha1加密
    3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

    由于这个东西在接收消息时是通用的,我们可以使用授权过滤器AuthorizeAttribute来实现。

    using System.Configuration;
    using System.Net;
    using System.Net.Http;
    using System.Web;
    using System.Web.Http;
    using System.Linq;
    using System.Web.Http.Controllers;
    
    using Efh.Core.Security;
    
    namespace Efh.Blog.Web.Areas.WeiXin.Filter
    {
        public class WXAuthorizeAttribute : AuthorizeAttribute
        {
            /// <summary>
            /// 签名Key
            /// </summary>
            private string _wxToken = ConfigurationManager.AppSettings["WXToken"];
    
            /// <summary>
            /// 是否通过授权
            /// </summary>
            /// <param name="actionContext">上下文</param>
            /// <returns>是否成功</returns>
            protected override bool IsAuthorized(HttpActionContext actionContext)
            {
                var requestQueryPairs = actionContext.Request.GetQueryNameValuePairs().ToDictionary(k => k.Key, v => v.Value);
                if (requestQueryPairs.Count == 0
                    || !requestQueryPairs.ContainsKey("timestamp")
                    || !requestQueryPairs.ContainsKey("signature")
                    || !requestQueryPairs.ContainsKey("nonce"))
                {
                    return false;
                }
    
                string[] waitEncryptParamsArray = new[] { _wxToken, requestQueryPairs["timestamp"], requestQueryPairs["nonce"] };
    
                string waitEncryptParamStr = string.Join("", waitEncryptParamsArray.OrderBy(m => m));
    
                string encryptStr = HashAlgorithm.SHA1(waitEncryptParamStr);
    
                return encryptStr.ToLower().Equals(requestQueryPairs["signature"].ToLower());
            }
    
            /// <summary>
            /// 处理未授权请求
            /// </summary>
            /// <param name="actionContext">上下文</param>
            protected sealed override void HandleUnauthorizedRequest(HttpActionContext actionContext)
            {
                actionContext.Response = actionContext.Request.CreateResponse(
                    HttpStatusCode.Unauthorized, new { status = "sign_error" });
            }
        }
    }
    

    将该特性声明在我们的微信Controller或者Action上,我们的签名校验便完成了。

    二、首次提交验证申请

    首次提交验证申请,微信服务器来调的时候是Get请求,而且要求我们将echostr原样返回。
    注意,是原样返回。不是XML,也不是Json,<string>echostr</string>和"echostr"都是不行的!

    囊中羞涩,本人使用的是虚拟主机搭载在原有的项目中,故新建微信区域(WeiXin)来实现。WeiXinAreaRegistration.cs文件如下:

    public class WeiXinAreaRegistration : AreaRegistration
    {
        public override string AreaName
        {
            get
            {
                return "WeiXin";
            }
        }
    
        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.Routes.MapHttpRoute(
                "WeiXinProcessor",
                "WeiXin/{controller}",
                new { controller = "Processor" }
            );
        }
    }
    

    新建Processor控制器,实现如下:

    [WXAuthorize]
    public class ProcessorController : ApiController
    {
    	public HttpResponseMessage Get()
    	{
    	    var requestQueryPairs = Request.GetQueryNameValuePairs().ToDictionary(k => k.Key, v => v.Value);
    	
    	    return new HttpResponseMessage(HttpStatusCode.OK)
    	    {
    	        Content = new StringContent(requestQueryPairs["echostr"]),
    	    };
    	}
    }
    

    上述我们便实现了首次微信的验证。

    三、接收消息

    微信将请求的消息分为六种:文本消息、图片消息、语音消息、视频消息、地理位置消息、链接消息,其实我们还可以将事件推送也理解为其中一种。

    将响应的消息分为六种:
    1. 回复文本消息
    2. 回复图片消息
    3. 回复语音消息
    4. 回复视频消息
    5. 回复音乐消息
    6. 回复图文消息
    。我们在这儿主要使用文本消息和图文消息。

    分析后我们发现,ToUserNameFromUserNameCreateTimeMsgType是所有消息共有的参数。同时也是我们响应时必需的参数。

    我们创建消息基类和消息类型枚举如下

    public class BaseMsg
    {
        public string ToUserName { get; set; }
    
        public string FromUserName { get; set; }
    
        public long CreateTime { get; set; }
    
        public MsgType MsgType { get; set; }
    }
    
    public enum MsgType
    {
        [XmlEnum("event")]
        Event,
        [XmlEnum("text")]
        Text,
        [XmlEnum("image")]
        Image,
        [XmlEnum("voice")]
        Voice,
        [XmlEnum("video")]
        Video,
        [XmlEnum("music")]
        Music,
        [XmlEnum("news")]
        News
    }
    

    此处枚举字段标注的XmlEnum稍候解释。

    而后按照各消息类型的非共有的参数,分别创建对应消息的实体类

    文本消息:

    [XmlRoot("xml")]
    public class TextMsg : BaseMsg
    {
        public string Content { get; set; }
    }
    

    图文消息:

    [XmlRoot("xml")]
    public class NewsMsg : BaseMsg
    {
        public int ArticleCount { get; set; }
    
        [XmlArray("Articles")]
        [XmlArrayItem("item")]
        public List<NewsInfo> Articles { get; set; }
    }
    
    public class NewsInfo
    {
        public string Title { get; set; }
    
        public string Description { get; set; }
    
        public string PicUrl { get; set; }
    
        public string Url { get; set; }
    }
    

    等等。

    刚才下班,朋友喊了,勿勿忙忙就提交了。。。现在继续!

    接下来我们就可以开始接收微信的消息了

    微信是通过Post,从正文中以XML的格式将参数传递过来的

    var requestContent = Request.Content.ReadAsStreamAsync().Result;
    

    将正文参数读取出来后,转为Xml

    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;//我们的用户标识
    

    而后,我们根据消息类型,进行进一步的处理。

    静候片刻,第二篇马上奉上...

  • 相关阅读:
    mongodb备份与恢复
    MongoDB-3.4安装文档
    (转)Zabbix 3.2.7编译安装记录
    (转)error while loading shared libraries:libmysqlclient.so.18 错误
    (转)如何使用Journalctl查看并操作Systemd日志
    (转)基于CentOS 7安装Zabbix 3.4和Zabbix4.0
    (转)yum安装MariaDB(使用国内镜像快速安装,三分钟安装完毕)
    (转)nmon和nmon analyser的下载和使用
    (转)Db2 数据库常见堵塞问题分析和处理
    (转)我是一个线程
  • 原文地址:https://www.cnblogs.com/efenghuo/p/4244805.html
Copyright © 2011-2022 走看看