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

而后,我們根據消息類型,進行進一步的處理。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM