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

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

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

  • 相关阅读:
    八数码难题 (codevs 1225)题解
    小木棍 (codevs 3498)题解
    sliding windows (poj 2823) 题解
    集合删数 (vijos 1545) 题解
    合并果子 (codevs 1063) 题解
    等价表达式 (codevs 1107)题解
    生理周期 (poj 1006) 题解
    区间 (vijos 1439) 题解
    区间覆盖问题 题解
    种树 (codevs 1653) 题解
  • 原文地址:https://www.cnblogs.com/efenghuo/p/4244805.html
Copyright © 2011-2022 走看看