使用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;//咱們的用戶標識

然後,咱們根據消息類型,進行進一步的處理。

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

相關文章
相關標籤/搜索