用c#開發微信 (4) 基於Senparc.Weixin框架的接收事件推送處理 (源碼下載)


本文講述使用Senparc.Weixin框架來快速處理各種接收事件推送。這里的消息指的是傳統的微信公眾平台消息交互,微信用戶向公眾號發送消息后,公眾號回復消息給微信用戶。包括以下類型:

1 subscribe/unsubscribe: 關注/取消關注事件
2 scan: 掃描帶參數二維碼事件
3 location: 上報地理位置事件
4 click: 自定義菜單事件
    1) click: 點擊菜單拉取消息時的事件推送
    2) view: 點擊菜單跳轉鏈接時的事件推送
    3)  scancode_push:掃碼推事件的事件推送
    4)  scancode_waitmsg:掃碼推事件且彈出“消息接收中”提示框的事件推送
    5)  pic_sysphoto:彈出系統拍照發圖的事件推送
    6)  pic_photo_or_album:彈出拍照或者相冊發圖的事件推送
    7)  pic_weixin:彈出微信相冊發圖器的事件推送
    8)  location_select:彈出地理位置選擇器的事件推送

實現非常簡單,自定義一個繼承MessageHandler的類,重寫這些類型的方法即可。注意:DefaultResponseMessage必須重寫,用於返回沒有處理過的消息類型(也可以用於默認消息,如幫助信息等);其中所有原OnXX的抽象方法已經都改為虛方法,可以不必每個都重寫。若不重寫,默認返回DefaultResponseMessage方法中的結果。

 

下面詳細介紹實現步驟:

1. 添加index頁面

private readonly string Token = ConfigurationManager.AppSettings["token"];//與微信公眾賬號后台的Token設置保持一致,區分大小寫。
       protected void Page_Load(object sender, EventArgs e)
       {
           string signature = Request["signature"];
           string timestamp = Request["timestamp"];
           string nonce = Request["nonce"];
           string echostr = Request["echostr"];
           if (Request.HttpMethod == "GET")
           {
               //get method - 僅在微信后台填寫URL驗證時觸發
               if (CheckSignature.Check(signature, timestamp, nonce, Token))
               {
                   WriteContent(echostr); //返回隨機字符串則表示驗證通過
               }
               else
               {
                   WriteContent("failed:" + signature + "," + CheckSignature.GetSignature(timestamp, nonce, Token) + "。" +
                               "如果你在瀏覽器中看到這句話,說明此地址可以被作為微信公眾賬號后台的Url,請注意保持Token一致。");
               }
               Response.End();
           }
           else
           {
               //post method - 當有用戶想公眾賬號發送消息時觸發
               if (!CheckSignature.Check(signature, timestamp, nonce, Token))
               {
                   WriteContent("參數錯誤!");
                   return;
               }
               //設置每個人上下文消息儲存的最大數量,防止內存占用過多,如果該參數小於等於0,則不限制
               var maxRecordCount = 10;
               //自定義MessageHandler,對微信請求的詳細判斷操作都在這里面。
               var messageHandler = new CustomMessageHandler(Request.InputStream, maxRecordCount);
 
               try
               {
                   //測試時可開啟此記錄,幫助跟蹤數據,使用前請確保App_Data文件夾存在,且有讀寫權限。
                   messageHandler.RequestDocument.Save(
                       Server.MapPath("~/App_Data/" + DateTime.Now.Ticks + "_Request_" +
                                      messageHandler.RequestMessage.FromUserName + ".txt"));
                   //執行微信處理過程
                   messageHandler.Execute();
                   //測試時可開啟,幫助跟蹤數據
                   messageHandler.ResponseDocument.Save(
                       Server.MapPath("~/App_Data/" + DateTime.Now.Ticks + "_Response_" +
                                      messageHandler.ResponseMessage.ToUserName + ".txt"));
                   WriteContent(messageHandler.ResponseDocument.ToString());
                   return;
               }
               catch (Exception ex)
               {
                   //將程序運行中發生的錯誤記錄到App_Data文件夾
                   using (TextWriter tw = new StreamWriter(Server.MapPath("~/App_Data/Error_" + DateTime.Now.Ticks + ".txt")))
                   {
                       tw.WriteLine(ex.Message);
                       tw.WriteLine(ex.InnerException.Message);
                       if (messageHandler.ResponseDocument != null)
                       {
                           tw.WriteLine(messageHandler.ResponseDocument.ToString());
                       }
                       tw.Flush();
                       tw.Close();
                   }
                   WriteContent("");
               }
               finally
               {
                   Response.End();
               }
           }
       }
       private void WriteContent(string str)
       {
           Response.Output.Write(str);
       }

 

1)當Get請求時,調用 CheckSignature.Check(signature, timestamp, nonce, Token) 方法驗證url接入, 詳情參考 用c#開發微信(1)服務號的服務器配置和企業號的回調模式 - url接入 (源碼下載)

2)  當有Post請求過來時,調用自定義MessageHandler類,對微信請求的詳細判斷操作都在這里面。

var messageHandler = new CustomMessageHandler(Request.InputStream, maxRecordCount);

messageHandler.Execute();

 

2. 自定義消息處理類

定義CustomMessageHandler繼承MessageHandler<MessageContext<IRequestMessageBase, IResponseMessageBase>>

public partial class CustomMessageHandler : MessageHandler<MessageContext<IRequestMessageBase, IResponseMessageBase>>
{
    public CustomMessageHandler(Stream inputStream, int maxRecordCount = 0)
        : base(inputStream, null, maxRecordCount)
    {
        WeixinContext.ExpireMinutes = 3;
    }
    public override void OnExecuting()
    {
        //測試MessageContext.StorageData
        if (CurrentMessageContext.StorageData == null)
        {
            CurrentMessageContext.StorageData = 0;
        }
        base.OnExecuting();
    }
    public override void OnExecuted()
    {
        base.OnExecuted();
        CurrentMessageContext.StorageData = ((int)CurrentMessageContext.StorageData) + 1;
    }
}

 

3. 分別重寫幾種接收事件推送

我們可以通過重寫MessageHandler里的這幾種類型方法來處理我們的業務,當然也可以只重寫需要的部分類型,不需要的類型可以不重寫,只需要定義一個統一的DefaultResponseMessage

public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
{
    //所有沒有被處理的消息會默認返回這里的結果
    var responseMessage = this.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "這條消息來自DefaultResponseMessage。";
    return responseMessage;
}

 

下面分別就這幾種類型,各寫一個例子:

1) 關注事件

/// <summary>
        /// 訂閱(關注)事件
        /// </summary>
        /// <returns></returns>
        public override IResponseMessageBase OnEvent_SubscribeRequest(RequestMessageEvent_Subscribe requestMessage)
        {
            var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
            responseMessage.Content = GetWelcomeInfo();
            return responseMessage;
        }

2) 取消關注事件

/// <summary>
        /// 退訂
        /// 實際上用戶無法收到非訂閱賬號的消息,所以這里可以隨便寫。
        /// unsubscribe事件的意義在於及時刪除網站應用中已經記錄的OpenID綁定,消除冗余數據。並且關注用戶流失的情況。
        /// </summary>
        /// <returns></returns>
        public override IResponseMessageBase OnEvent_UnsubscribeRequest(RequestMessageEvent_Unsubscribe requestMessage)
        {
            var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
            responseMessage.Content = "有空再來";
            return responseMessage;
        }

 

3) 掃描帶參數二維碼事件

public override IResponseMessageBase OnEvent_ScanRequest(RequestMessageEvent_Scan requestMessage)
{
    //通過掃描關注
    var responseMessage = CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "通過掃描關注。";
    return responseMessage;
}

 

4) 上報地理位置事件

public override IResponseMessageBase OnEvent_LocationRequest(RequestMessageEvent_Location requestMessage)
{
    //這里是微信客戶端(通過微信服務器)自動發送過來的位置信息
    var responseMessage = CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "這里寫什么都無所謂,比如:上帝愛你!";
    return responseMessage;//這里也可以返回null(需要注意寫日志時候null的問題)
}

 

4. 自定義菜單事件推送

用戶點擊自定義菜單后,微信會把點擊事件推送給開發者,請注意,點擊菜單彈出子菜單,不會產生上報。請注意,第3個到第8個的所有事件,僅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用戶,舊版本微信用戶點擊后將沒有回應,開發者也不能正常接收到事件推送。

1) 點擊菜單拉取消息時的事件推送

public override IResponseMessageBase OnTextOrEventRequest(RequestMessageText requestMessage)
{
    // 預處理文字或事件類型請求。
    // 這個請求是一個比較特殊的請求,通常用於統一處理來自文字或菜單按鈕的同一個執行邏輯,
    // 會在執行OnTextRequest或OnEventRequest之前觸發,具有以下一些特征:
    // 1、如果返回null,則繼續執行OnTextRequest或OnEventRequest
    // 2、如果返回不為null,則終止執行OnTextRequest或OnEventRequest,返回最終ResponseMessage
    // 3、如果是事件,則會將RequestMessageEvent自動轉為RequestMessageText類型,其中RequestMessageText.Content就是RequestMessageEvent.EventKey
 
    if (requestMessage.Content == "OneClick")
    {
        var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
        strongResponseMessage.Content = "您點擊了底部按鈕。\r\n為了測試微信軟件換行bug的應對措施,這里做了一個——\r\n換行";
        return strongResponseMessage;
    }
    return null;//返回null,則繼續執行OnTextRequest或OnEventRequest
}
 
public override IResponseMessageBase OnEvent_ClickRequest(RequestMessageEvent_Click requestMessage)
{
    IResponseMessageBase reponseMessage = null;
    //菜單點擊,需要跟創建菜單時的Key匹配
    switch (requestMessage.EventKey)
    {
        case "OneClick":
            {
                //這個過程實際已經在OnTextOrEventRequest中完成,這里不會執行到。
                var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Content = "您點擊了底部按鈕。\r\n為了測試微信軟件換行bug的應對措施,這里做了一個——\r\n換行";
            }
            break;
        case "SubClickRoot_Text":
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Content = "您點擊了子菜單按鈕。";
            }
            break;
        case "SubClickRoot_News":
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageNews>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Articles.Add(new Article()
                {
                    Title = "您點擊了子菜單圖文按鈕",
                    Description = "您點擊了子菜單圖文按鈕,這是一條圖文信息。",
                    PicUrl = "http://weixin.senparc.com/Images/qrcode.jpg",
                    Url = "http://weixin.senparc.com"
                });
            }
            break;
        case "SubClickRoot_Music":
            {
                //上傳縮略圖
                var accessToken = CommonAPIs.AccessTokenContainer.TryGetToken(appId, appSecret);
                var uploadResult = AdvancedAPIs.Media.MediaApi.UploadTemporaryMedia(accessToken, UploadMediaFileType.thumb,
                                                             Server.GetMapPath("~/Images/Logo.jpg"));
                //設置音樂信息
                var strongResponseMessage = CreateResponseMessage<ResponseMessageMusic>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Music.Title = "天籟之音";
                strongResponseMessage.Music.Description = "真的是天籟之音";
                strongResponseMessage.Music.MusicUrl = "http://weixin.senparc.com/Content/music1.mp3";
                strongResponseMessage.Music.HQMusicUrl = "http://weixin.senparc.com/Content/music1.mp3";
                strongResponseMessage.Music.ThumbMediaId = uploadResult.thumb_media_id;
            }
            break;
        case "SubClickRoot_Image":
            {
                //上傳圖片
                var accessToken = CommonAPIs.AccessTokenContainer.TryGetToken(appId, appSecret);
                var uploadResult = AdvancedAPIs.Media.MediaApi.UploadTemporaryMedia(accessToken, UploadMediaFileType.image,
                                                             Server.GetMapPath("~/Images/Logo.jpg"));
                //設置圖片信息
                var strongResponseMessage = CreateResponseMessage<ResponseMessageImage>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Image.MediaId = uploadResult.media_id;
            }
            break;
        case "SubClickRoot_Agent"://代理消息
            {
                //獲取返回的XML
                DateTime dt1 = DateTime.Now;
                reponseMessage = MessageAgent.RequestResponseMessage(this, agentUrl, agentToken, RequestDocument.ToString());
                //上面的方法也可以使用擴展方法:this.RequestResponseMessage(this,agentUrl, agentToken, RequestDocument.ToString());
 
                DateTime dt2 = DateTime.Now;
 
                if (reponseMessage is ResponseMessageNews)
                {
                    (reponseMessage as ResponseMessageNews)
                        .Articles[0]
                        .Description += string.Format("\r\n\r\n代理過程總耗時:{0}毫秒", (dt2 - dt1).Milliseconds);
                }
            }
            break;
        case "Member"://托管代理會員信息
            {
                //原始方法為:MessageAgent.RequestXml(this,agentUrl, agentToken, RequestDocument.ToString());//獲取返回的XML
                reponseMessage = this.RequestResponseMessage(agentUrl, agentToken, RequestDocument.ToString());
            }
            break;
        case "OAuth"://OAuth授權測試
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageNews>();
                strongResponseMessage.Articles.Add(new Article()
                {
                    Title = "OAuth2.0測試",
                    Description = "點擊【查看全文】進入授權頁面。\r\n注意:此頁面僅供測試(是專門的一個臨時測試賬號的授權,並非Senparc.Weixin.MP SDK官方賬號,所以如果授權后出現錯誤頁面數正常情況),測試號隨時可能過期。請將此DEMO部署到您自己的服務器上,並使用自己的appid和secret。",
                    Url = "http://weixin.senparc.com/oauth2",
                    PicUrl = "http://weixin.senparc.com/Images/qrcode.jpg"
                });
                reponseMessage = strongResponseMessage;
            }
            break;
        case "Description":
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
                strongResponseMessage.Content = GetWelcomeInfo();
                reponseMessage = strongResponseMessage;
            }
            break;
        case "SubClickRoot_PicPhotoOrAlbum":
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Content = "您點擊了【微信拍照】按鈕。系統將會彈出拍照或者相冊發圖。";
            }
            break;
        case "SubClickRoot_ScancodePush":
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Content = "您點擊了【微信掃碼】按鈕。";
            }
            break;
        default:
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
                strongResponseMessage.Content = "您點擊了按鈕,EventKey:" + requestMessage.EventKey;
                reponseMessage = strongResponseMessage;
            }
            break;
    }
 
    return reponseMessage;
}

 

2) 點擊菜單跳轉鏈接時的事件推送

public override IResponseMessageBase OnEvent_ViewRequest(RequestMessageEvent_View requestMessage)
{
   //說明:這條消息只作為接收,下面的responseMessage到達不了客戶端,類似OnEvent_UnsubscribeRequest
   var responseMessage = CreateResponseMessage<ResponseMessageText>();
   responseMessage.Content = "您點擊了view按鈕,將打開網頁:" + requestMessage.EventKey;
   return responseMessage;
}

 

3) 掃碼推事件的事件推送

/// <summary>
/// 事件之掃碼推事件(scancode_push)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ScancodePushRequest(RequestMessageEvent_Scancode_Push requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之掃碼推事件";
    return responseMessage;
}

4) 掃碼推事件且彈出“消息接收中”提示框的事件推送

/// <summary>
/// 事件之掃碼推事件且彈出“消息接收中”提示框(scancode_waitmsg)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ScancodeWaitmsgRequest(RequestMessageEvent_Scancode_Waitmsg requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之掃碼推事件且彈出“消息接收中”提示框";
    return responseMessage;
}

5) 彈出系統拍照發圖的事件推送

/// <summary>
/// 事件之彈出系統拍照發圖(pic_sysphoto)
/// 實際測試時發現微信並沒有推送RequestMessageEvent_Pic_Sysphoto消息,只能接收到用戶在微信中發送的圖片消息。
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_PicSysphotoRequest(RequestMessageEvent_Pic_Sysphoto requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之彈出系統拍照發圖";
    return responseMessage;
}

6) 彈出拍照或者相冊發圖的事件推送

/// <summary>
/// 事件之彈出拍照或者相冊發圖(pic_photo_or_album)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_PicPhotoOrAlbumRequest(RequestMessageEvent_Pic_Photo_Or_Album requestMessage)
{
   var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
   responseMessage.Content = "事件之彈出拍照或者相冊發圖";
   return responseMessage;
}

7) 彈出微信相冊發圖器的事件推送

/// <summary>
/// 事件之彈出微信相冊發圖器(pic_weixin)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_PicWeixinRequest(RequestMessageEvent_Pic_Weixin requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之彈出微信相冊發圖器";
    return responseMessage;
}

8) 彈出地理位置選擇器的事件推送

/// <summary>
/// 事件之彈出地理位置選擇器(location_select)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_LocationSelectRequest(RequestMessageEvent_Location_Select requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之彈出地理位置選擇器";
    return responseMessage;
}
 

5. 最后發布到自己的服務器上,可以嘗試給公眾號發送各種接收事件,驗證公眾號回復的內容

 

6. 源碼

最后整個程序結構如下:

image

 

這里的CustomMessageHandler_Events.cs和CustomMessageHandler.cs是同一個類CustomMessageHandler;  一個處理事件,一個處理消息。

 

源碼下載: http://yunpan.cn/cwKCaYahyczTF  訪問密碼 336e

同樣的,使用源碼前,要先把配置文件里的參數修改成自己的公眾號。

 

關注事件效果圖:

 Screenshot_2015-05-23-10-40-55

用c#開發微信 系列匯總


免責聲明!

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



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