本文實例講述了C#微信公眾號開發之接收事件推送與訊息排重的方法。分享給大家供大家參考。具體分析如下:
微信伺服器在5秒內收不到回應會斷掉連接,並且重新發起請求,總共重試三次。這樣的話,問題就來了。有這樣一個場景:當用戶關注微信帳號時,獲取當前用戶信息,然後將信息寫到資料庫中。類似pc端網站的註冊。可能由於這個關注事件中,我們需要處理的業務邏輯比較複雜。如送積分啊,寫用戶日誌啊,分配用戶群組啊。等等……一系列的邏輯需要執行,或者網路環境比較複雜,無法保證5秒內回應當前用戶的操作,那如果當操作尚未完成,微信伺服器又給我們的伺服器推送了一條相同的關注事件,我們將再次執行我們的那些邏輯,這樣就有可能導致資料庫中出現重複的資料(有的童鞋就會說了,我在插入資料之前先判斷目前是否已經存在了,如果存在了就不執行插入的操作。重要性。
訊息的去重普通訊息和事件訊息是有區別的。普通訊息使用msgid,而事件訊息使用FromUserName + CreateTime。我的想法是:
新建類別BaseMsg,有三個屬性分別是FromUser,MsgFlag,CreateTime。程式碼如下:
public class BaseMsg { /// <summary> /// 发送者标识 /// </summary> public string FromUser { get; set; } /// <summary> /// 消息表示。普通消息时,为msgid,事件消息时,为事件的创建时间 /// </summary> public string MsgFlag { get; set; } /// <summary> /// 添加到队列的时间 /// </summary> public DateTime CreateTime { get; set; } }
建立個靜態列表_queue,用來儲存訊息列表,列表的類型是List
在處理微信訊息體前,先判斷列表是否實例化,如果沒有實例化則實例化,否則判斷清單的長度是否大於或等於50(這個可以自訂,用處就是微信並發的訊息量),如果大於或等於50,則保留20秒內未回應的訊息(5秒重試一次,總共重試3次,就是15秒,保險起見這裡寫20秒)。
取得目前訊息體的訊息類型,並根據_queue判斷目前訊息是否已經要求了。如果是事件則儲存FromUser和建立時間。如果是普通訊息則儲存MsgFlag。下面是程式碼:
if (_queue == null) { _queue = new List<basemsg>(); } else if(_queue.Count>=50) { _queue = _queue.Where(q => { return q.CreateTime.AddSeconds(20) > DateTime.Now; }).ToList();//保留20秒内未响应的消息 } XElement xdoc = XElement.Parse(xml); var msgtype = xdoc.Element("MsgType").Value.ToUpper(); var FromUserName = xdoc.Element("FromUserName").Value; var MsgId = xdoc.Element("MsgId").Value; var CreateTime = xdoc.Element("CreateTime").Value; MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype); if (type!=MsgType.EVENT) { if (_queue.FirstOrDefault(m => { return m.MsgFlag == MsgId; }) == null) { _queue.Add(new BaseMsg { CreateTime = DateTime.Now, FromUser = FromUserName, MsgFlag = MsgId }); } else { return null; } } else { if (_queue.FirstOrDefault(m => { return m.MsgFlag == CreateTime; }) == null) { _queue.Add(new BaseMsg { CreateTime = DateTime.Now, FromUser = FromUserName, MsgFlag = CreateTime }); } else { return null; } }</basemsg>
當訊息已經存在佇列中時,則不轉換目前的訊息為實體了,直接傳回null,呼叫的時候,當傳回null時就不做任何處理。
下面開始講解事件訊息。接上篇講。所有的訊息都繼承BaseMessage,而所有的事件類型都包含一個Event的屬性。這裡為了方便調用,將訊息
/// <summary> /// 事件类型枚举 /// </summary> public enum Event { /// <summary> /// 非事件类型 /// </summary> NOEVENT, /// <summary> /// 订阅 /// </summary> SUBSCRIBE, /// <summary> /// 取消订阅 /// </summary> UNSUBSCRIBE, /// <summary> /// 扫描带参数的二维码 /// </summary> SCAN, /// <summary> /// 地理位置 /// </summary> LOCATION, /// <summary> /// 单击按钮 /// </summary> CLICK, /// <summary> /// 链接按钮 /// </summary> VIEW, /// <summary> /// 扫码推事件 /// </summary> SCANCODE_PUSH, /// <summary> /// 扫码推事件且弹出“消息接收中”提示框 /// </summary> SCANCODE_WAITMSG, /// <summary> /// 弹出系统拍照发图 /// </summary> PIC_SYSPHOTO, /// <summary> /// 弹出拍照或者相册发图 /// </summary> PIC_PHOTO_OR_ALBUM, /// <summary> /// 弹出微信相册发图器 /// </summary> PIC_WEIXIN, /// <summary> /// 弹出地理位置选择器 /// </summary> LOCATION_SELECT, /// <summary> /// 模板消息推送 /// </summary> TEMPLATESENDJOBFINISH }
定義好枚舉後,就是定義訊息實體了。
追蹤/取消追蹤事件
xml資料包如下:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[FromUser]]></FromUserName> <CreateTime>123456789</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[subscribe]]></Event> </xml>
對應的實體:
/// <summary> /// 订阅/取消订阅事件 /// </summary> public class SubEventMessage : EventMessage { private string _eventkey; /// <summary> /// 事件KEY值,qrscene_为前缀,后面为二维码的参数值(已去掉前缀,可以直接使用) /// </summary> public string EventKey { get { return _eventkey; } set { _eventkey = value.Replace("qrscene_", ""); } } /// <summary> /// 二维码的ticket,可用来换取二维码图片 /// </summary> public string Ticket { get; set; } }
這裡要注意的是,當用戶掃描帶參數的二維碼時,如果用戶沒有關注當前公眾號,用戶關注時,會在訊息體中帶上qrscene_參數,和Ticket,所以這裡定義了兩個屬性:EventKey,Ticket。當EventKey被賦值時,替換掉qrscene_,因為我們真正需要的就是後面的參數。
掃描帶參數二維碼事件
用戶掃描帶場景值二維碼時,可能推送一下兩種事件:
如果用戶還未關注公眾號,則用戶可以關注公眾號,關注後微信會將帶場景值關注事件推送給開發者。
如果使用者已經關注公眾號,則微信會將帶場景值掃描事件推送給開發者。 、
第一種上面已經講了,這裡就只說明下第二種。
用戶已關注時的事件推送
xml包如下:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[FromUser]]></FromUserName> <CreateTime>123456789</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[SCAN]]></Event> <EventKey><![CDATA[SCENE_VALUE]]></EventKey> <Ticket><![CDATA[TICKET]]></Ticket> </xml>
對應的實體如下:
/// <summary> /// 扫描带参数的二维码实体 /// </summary> public class ScanEventMessage : EventMessage { /// <summary> /// 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id /// </summary> public string EventKey { get; set; } /// <summary> /// 二维码的ticket,可用来换取二维码图片 /// </summary> public string Ticket { get; set; } }
上報地理位置事件
當公眾號開啟上報用戶同意上報地理位置後,都會在進入時上報地理位置,或在進入回話後每5秒上報一次地理位置,公眾號可以再公眾平台的後台中修改設定。上報地理位置時,微信會將上報地理位置事件推播到開發者填寫的url。
xml封包如下:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>123456789</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[LOCATION]]></Event> <Latitude>23.137466</Latitude> <Longitude>113.352425</Longitude> <Precision>119.385040</Precision> </xml>
對應的實體如下:
/// <summary> /// 上报地理位置实体 /// </summary> public class LocationEventMessage : EventMessage { /// <summary> /// 地理位置纬度 /// </summary> public string Latitude { get; set; } /// <summary> /// 地理位置经度 /// </summary> public string Longitude { get; set; } /// <summary> /// 地理位置精度 /// </summary> public string Precision { get; set; } }
自訂選單事件常用的事件有:click,view,scancode_puth,scancode_waitmsg,location_select。另外還有三種發圖的事件,由於並不常用,筆者也沒想到使用場景,再次就不一一講述了,有興趣的可以自己研究下,或者和我進行交流。
click事件推送的xml資料包:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[FromUser]]></FromUserName> <CreateTime>123456789</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[CLICK]]></Event> <EventKey><![CDATA[EVENTKEY]]></EventKey> </xml>
view事件推送的xml資料包和click的格式是一樣的,所以定義一個類別就可以了,如下:
/// <summary> /// 普通菜单事件,包括click和view /// </summary> public class NormalMenuEventMessage : EventMessage { /// <summary> /// 事件KEY值,设置的跳转URL /// </summary> public string EventKey { get; set; } }
scancode事件的xml資料包如下:
<xml><ToUserName><![CDATA[ToUserName]]></ToUserName> <FromUserName><![CDATA[FromUserName]]></FromUserName> <CreateTime>1419265698</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[scancode_push]]></Event> <EventKey><![CDATA[EventKey]]></EventKey> <ScanCodeInfo><ScanType><![CDATA[qrcode]]></ScanType> <ScanResult><![CDATA[http://weixin.qq.com/r/JEy5oRLE0U_urVbC9xk2]]></ScanResult> </ScanCodeInfo> </xml>
對應的實體如下:
/// <summary> /// 菜单扫描事件 /// </summary> public class ScanMenuEventMessage : EventMessage { /// <summary> /// 事件KEY值 /// </summary> public string EventKey { get; set; } /// <summary> /// 扫码类型。qrcode是二维码,其他的是条码 /// </summary> public string ScanType { get; set; } /// <summary> /// 扫描结果 /// </summary> public string ScanResult { get; set; } }
至此,目前常用的事件類型訊息都已定義完畢,結合上一篇所講的,將xml資料包轉換成物件的完整程式碼如下:
public class MessageFactory { private static List<BaseMsg> _queue; public static BaseMessage CreateMessage(string xml) { if (_queue == null) { _queue = new List<BaseMsg>(); } else if(_queue.Count>=50) { _queue = _queue.Where(q => { return q.CreateTime.AddSeconds(20) > DateTime.Now; }).ToList();//保留20秒内未响应的消息 } XElement xdoc = XElement.Parse(xml); var msgtype = xdoc.Element("MsgType").Value.ToUpper(); var FromUserName = xdoc.Element("FromUserName").Value; var MsgId = xdoc.Element("MsgId").Value; var CreateTime = xdoc.Element("CreateTime").Value; MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype); if (type!=MsgType.EVENT) { if (_queue.FirstOrDefault(m => { return m.MsgFlag == MsgId; }) == null) { _queue.Add(new BaseMsg { CreateTime = DateTime.Now, FromUser = FromUserName, MsgFlag = MsgId }); } else { return null; } } else { if (_queue.FirstOrDefault(m => { return m.MsgFlag == CreateTime; }) == null) { _queue.Add(new BaseMsg { CreateTime = DateTime.Now, FromUser = FromUserName, MsgFlag = CreateTime }); } else { return null; } } switch (type) { case MsgType.TEXT: return Utils.ConvertObj<TextMessage>(xml); case MsgType.IMAGE: return Utils.ConvertObj<ImgMessage>(xml); case MsgType.VIDEO: return Utils.ConvertObj<VideoMessage>(xml); case MsgType.VOICE: return Utils.ConvertObj<VoiceMessage>(xml); case MsgType.LINK: return Utils.ConvertObj<LinkMessage>(xml); case MsgType.LOCATION: return Utils.ConvertObj<LocationMessage>(xml); case MsgType.EVENT://事件类型 { var eventtype = (Event)Enum.Parse(typeof(Event), xdoc.Element("Event").Value.ToUpper()); switch (eventtype) { case Event.CLICK: return Utils.ConvertObj<NormalMenuEventMessage>(xml); case Event.VIEW: return Utils.ConvertObj<NormalMenuEventMessage>(xml); case Event.LOCATION: return Utils.ConvertObj<LocationEventMessage>(xml); case Event.LOCATION_SELECT: return Utils.ConvertObj<LocationMenuEventMessage>(xml); case Event.SCAN: return Utils.ConvertObj<ScanEventMessage>(xml); case Event.SUBSCRIBE: return Utils.ConvertObj<SubEventMessage>(xml); case Event.UNSUBSCRIBE: return Utils.ConvertObj<SubEventMessage>(xml); case Event.SCANCODE_WAITMSG: return Utils.ConvertObj<ScanMenuEventMessage>(xml); default: return Utils.ConvertObj<EventMessage>(xml); } } break; default: return Utils.ConvertObj<BaseMessage>(xml); } } }
希望本文所述對大家基於C#的微信開發有所幫助。
更多C#微信公眾號開發之接收事件推播與訊息排重的方法相關文章請關注PHP中文網!

c#.netissutableforenterprise-levelapplications withemofrosoftecosystemdueToItsStrongTyping,richlibraries,androbustperraries,androbustperformance.however,itmaynotbeidealfoross-platement forment forment forment forvepentment offependment dovelopment toveloperment toveloperment whenrawspeedsportor whenrawspeedseedpolitical politionalitable,

C#在.NET中的編程過程包括以下步驟:1)編寫C#代碼,2)編譯為中間語言(IL),3)由.NET運行時(CLR)執行。 C#在.NET中的優勢在於其現代化語法、強大的類型系統和與.NET框架的緊密集成,適用於從桌面應用到Web服務的各種開發場景。

C#是一種現代、面向對象的編程語言,由微軟開發並作為.NET框架的一部分。 1.C#支持面向對象編程(OOP),包括封裝、繼承和多態。 2.C#中的異步編程通過async和await關鍵字實現,提高應用的響應性。 3.使用LINQ可以簡潔地處理數據集合。 4.常見錯誤包括空引用異常和索引超出範圍異常,調試技巧包括使用調試器和異常處理。 5.性能優化包括使用StringBuilder和避免不必要的裝箱和拆箱。

C#.NET應用的測試策略包括單元測試、集成測試和端到端測試。 1.單元測試確保代碼的最小單元獨立工作,使用MSTest、NUnit或xUnit框架。 2.集成測試驗證多個單元組合的功能,常用模擬數據和外部服務。 3.端到端測試模擬用戶完整操作流程,通常使用Selenium進行自動化測試。

C#高級開發者面試需要掌握異步編程、LINQ、.NET框架內部工作原理等核心知識。 1.異步編程通過async和await簡化操作,提升應用響應性。 2.LINQ以SQL風格操作數據,需注意性能。 3..NET框架的CLR管理內存,垃圾回收需謹慎使用。

C#.NET面試問題和答案包括基礎知識、核心概念和高級用法。 1)基礎知識:C#是微軟開發的面向對象語言,主要用於.NET框架。 2)核心概念:委託和事件允許動態綁定方法,LINQ提供強大查詢功能。 3)高級用法:異步編程提高響應性,表達式樹用於動態代碼構建。

C#.NET是構建微服務的熱門選擇,因為其生態系統強大且支持豐富。 1)使用ASP.NETCore創建RESTfulAPI,處理訂單創建和查詢。 2)利用gRPC實現微服務間的高效通信,定義和實現訂單服務。 3)通過Docker容器化微服務,簡化部署和管理。

C#和.NET的安全最佳實踐包括輸入驗證、輸出編碼、異常處理、以及身份驗證和授權。 1)使用正則表達式或內置方法驗證輸入,防止惡意數據進入系統。 2)輸出編碼防止XSS攻擊,使用HttpUtility.HtmlEncode方法。 3)異常處理避免信息洩露,記錄錯誤但不返回詳細信息給用戶。 4)使用ASP.NETIdentity和Claims-based授權保護應用免受未授權訪問。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

記事本++7.3.1
好用且免費的程式碼編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

SublimeText3漢化版
中文版,非常好用