使用web api开发微信公众号,调用图灵机器人接口(一)

此文将分两篇讲解,主要分为如下几步服务器

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

其实图灵机器人搭载微信公众号很简单,只须要把图灵的地址配到公众后台就能够了。
不过这样作以后也就没有任何扩展的可能了,所以本身实现一套!微信

1、签名校验

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

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

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

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

加密/校验流程以下:code

  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上,咱们的签名校验便完成了。xml

2、首次提交验证申请

首次提交验证申请,微信服务器来调的时候是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"]),
        };
    }
}

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

3、接收消息

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

将响应的消息分为六种:
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;//咱们的用户标识

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

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

相关文章
相关标签/搜索