概述
由於微信公眾平台的特殊機制,所有的資訊都由微信伺服器轉送而來,因此伺服器是無法使用Session對使用者會話的上下文進行管理的。
為此Senparc.WeiXin.MP SDK增加了上下文的模組,並整合到了MessageHandler中。
WeixinContext
WeixinContext是所有單一使用者上下文(MessageContext)實體的容器(暫且稱為全域上下文)。 WeixinContext本身不是靜態類,意味著您可以在同一個應用程式中建立多個上下文實體。
同時,一個靜態的WeixinContext實例被放入到MessageHandler68b68cd4f5c098c0c63d969fd4792098中,因此所有專案中由MessageHandler68b68cd4f5c098c0c63d969fd4792098派生的子類別中的WeixinContext是唯一的、全域的(註:TM為實作IMessageContext的類,包括SDK中已經提供的MessageContext)。
因此我們在任何一個實作了MessageHandler68b68cd4f5c098c0c63d969fd4792098的實例中(例如叫MyMessageHandler),都可以存取到一個類型和名稱都叫WeixinContext的物件。
WeixinContext用於保存所使用者的上下文(MessageContext),並且提供了一系列的方法,主要方法包括:
/// 631fb227578dfffda61e1fa4d04b7d25
/// 631fb227578dfffda61e1fa4d04b7d25
/// 重設所有情境參數,所有記錄會清除
/// < ;/summary> 18"{ oid store #.
...
}
/// 631fb227578dfffda61e1fa4d04b7d25
///在於操作TM佇列,及時移除過期訊息,並將最新活動的物件移到尾部
/// 039f3e95db2a684c7b74365531eb6044
//// 07767e6fd826e8d979ffc3a9d858631a使用者名稱( OpenId)8bb7487ae6a16a43571bc14c7fcf93c2
/// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3
private TM GetMessageContext(string )
private TM GetMessageContext
# /// 631fb227578dfffda61e1fa4d04b7d25
/// 631fb227578dfffda61e1fa4d04b7d25
/// 取得MessageContext
/// 039f3e95db2a684c7b74365531eb6044
&penlt//////edtin> ;/param>
/// e63a580229b63ffa691b29f71803cad5True:如果使用者不存在,則建立一個實例,並傳回這個最新的實例
/// False:使用者儲存在,則回傳null8bb7487ae6a16a43571bc14c7fcf93c2
/// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3
private TM GetMessageContext( Corring userName, bool createIfNotExist ## }
/// 631fb227578dfffda61e1fa4d04b7d25
/// 取得MessageContext,如果不存在,請使用requestMessage資訊初始化一個,並傳回原始實例
/ /// public TM GetMessageContext(IRequestMessageBase requestMessage)
# ///
//// 039f3e95db2a684c7b74365531eb6044
1/// 039f3e95db2a684c7b74365531eb6044
1/// <
public TM GetMessageContext(IResponseMessageBase responseMessage)
{
...
); // 記錄請求資訊
/ // 039f3e95db2a684c7b74365531eb6044
/// 2545ceab610d17bcaea08b4927a7eab9要求資訊8bb7487ae6a16a43571bc14c7fcf93c2
public void InsertMessage(IRequestMessage{ requestMessage) 系統
}
/// 631fb227578dfffda61e1fa4d04b7d25
/// 記錄回應資訊
/// 039f3e95db2a684c7b74365531eb6044 ">回應資訊8bb7487ae6a16a43571bc14c7fcf93c2
public void InsertMessage(IResponseMessageBase responseMessage)
{
;
/// 取得最新一條請求數據,若不存在,則回傳Null
/// 039f3e95db2a684c7b74365531eb6044
/// 07767e6fd826e8d979ffc3a9d858631a使用者名稱(OpenId)ea958c64f9fd88e22a02074fe798a5bc
/// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3
public IRequestMessageBase GetLastRequestMessage(string )
#
# // / 631fb227578dfffda61e1fa4d04b7d25
/// 取得最新回應數據,若不存在,則回傳Null
/// 039f3e95db2a684c7b74365531eb6044
; ;使用者名稱(OpenId)8bb7487ae6a16a43571bc14c7fcf93c2
/// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3
public IResponseMessageBase GetLastRes {Message(stringuser Name ## }
WeixinContext中有兩個用來儲存使用者上下文的物件:MessageCollection及MessageQueue。
這兩個物件中的元素集合是重疊的,但是MessageQueue對元素進行了排序,以便及時處理掉頂部過期的上下文。
ExpireMinutes用來定義上下文時間有效期,預設為90分鐘。可以在程式的任何地方設定設個參數,且立即生效。
PS:MessageQueue中刪除過期資料的邏輯以極高的效率運作,常規開發時無需考慮CPU佔用及物件衝突的問題(額外校驗時間是否逾時)。
MessageContext
MessageContext用於保存單一使用者的上下文訊息,被儲存在WeixinContext的MessageCollection及MessageQueue物件中。 IMessageContext定義如下:
/// <summary> /// 微信消息上下文(单个用户)接口 /// </summary> /// <typeparam name="TRequest">请求消息类型</typeparam> /// <typeparam name="TResponse">响应消息类型</typeparam> public interface IMessageContext<TRequest,TResponse> where TRequest : IRequestMessageBase where TResponse : IResponseMessageBase { /// <summary> /// 用户名(OpenID) /// </summary> string UserName { get; set; } /// <summary> /// 最后一次活动时间(用户主动发送Resquest请求的时间) /// </summary> DateTime LastActiveTime { get; set; } /// <summary> /// 接收消息记录 /// </summary> MessageContainer<TRequest> RequestMessages { get; set; } /// <summary> /// 响应消息记录 /// </summary> MessageContainer<TResponse> ResponseMessages { get; set; } /// <summary> /// 最大储存容量(分别针对RequestMessages和ResponseMessages) /// </summary> int MaxRecordCount { get; set; } /// <summary> /// 临时储存数据,如用户状态等,出于保持.net 3.5版本,这里暂不使用dynamic /// </summary> object StorageData { get; set; } /// <summary> /// 用于覆盖WeixinContext所设置的默认过期时间 /// </summary> Double? ExpireMinutes { get; set; } /// <summary> /// AppStore状态,系统属性,请勿操作 /// </summary> AppStoreState AppStoreState { get; set; } event EventHandler<WeixinContextRemovedEventArgs<TRequest, TResponse>> MessageContextRemoved; void OnRemoved(); }
您可以根據自己的需求創建自己的類,實現這個接口,並且被WeixinContext使用。當然如果你的要求不是那麼特殊,而且你比較懶的話,SDK提供了一個預設的MessageContext實作:
/// <summary> /// 微信消息上下文(单个用户) /// </summary> public class MessageContext<TRequest,TResponse>: IMessageContext<TRequest, TResponse> where TRequest : IRequestMessageBase where TResponse : IResponseMessageBase { private int _maxRecordCount; public string UserName { get; set; } public DateTime LastActiveTime { get; set; } public MessageContainer<TRequest> RequestMessages { get; set; } public MessageContainer<TResponse> ResponseMessages { get; set; } public int MaxRecordCount { get { return _maxRecordCount; } set { RequestMessages.MaxRecordCount = value; ResponseMessages.MaxRecordCount = value; _maxRecordCount = value; } } public object StorageData { get; set; } public Double? ExpireMinutes { get; set; } /// <summary> /// AppStore状态,系统属性,请勿操作 /// </summary> public AppStoreState AppStoreState { get; set; } public virtual event EventHandler<WeixinContextRemovedEventArgs<TRequest, TResponse>> MessageContextRemoved = null; /// <summary> /// 执行上下文被移除的事件 /// 注意:此事件不是实时触发的,而是等过期后任意一个人发过来的下一条消息执行之前触发。 /// </summary> /// <param name="e"></param> private void OnMessageContextRemoved(WeixinContextRemovedEventArgs<TRequest, TResponse> e) { EventHandler<WeixinContextRemovedEventArgs<TRequest, TResponse>> temp = MessageContextRemoved; if (temp != null) { temp(this, e); } } /// <summary> /// /// </summary> /// <param name="maxRecordCount">maxRecordCount如果小于等于0,则不限制</param> public MessageContext(/*MessageContainer<IRequestMessageBase> requestMessageContainer, MessageContainer<IResponseMessageBase> responseMessageContainer*/) { /* * 注意:即使使用其他类实现IMessageContext, * 也务必在这里进行下面的初始化,尤其是设置当前时间, * 这个时间关系到及时从缓存中移除过期的消息,节约内存使用 */ RequestMessages = new MessageContainer<TRequest>(MaxRecordCount); ResponseMessages = new MessageContainer<TResponse>(MaxRecordCount); LastActiveTime = DateTime.Now; } public virtual void OnRemoved() { var onRemovedArg = new WeixinContextRemovedEventArgs<TRequest, TResponse>(this); OnMessageContextRemoved(onRemovedArg); } }
上面的程式碼根據註解很好理解,需要說明一下的是StorageData。這是一個用於儲存任何和使用者上下文有關資料的容器,WeixinContext和IMessageContext沒有對它進行任何引用,完全由開發者決定裡面的內容(例如使用者執行到哪一步、或某個比較重要的位置資訊等等),類似Session的作用。
上述MessageContext41040d08b7519ac9aae356ee76ec31a0類別已經提供了比較完備的常用的訊息處理的方法,可以直接使用。如果有更特別的需求,需要自訂MessageContext,推薦以這個類別為基底類別進行必要的重寫,例如下面就是一個自訂的上下文類別:
public class CustomMessageContext : MessageContext<IRequestMessageBase,IResponseMessageBase> { public CustomMessageContext() { base.MessageContextRemoved += CustomMessageContext_MessageContextRemoved; } /// <summary> /// 当上下文过期,被移除时触发的时间 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void CustomMessageContext_MessageContextRemoved(object sender, Senparc.Weixin.Context.WeixinContextRemovedEventArgs<IRequestMessageBase,IResponseMessageBase> e) { /* 注意,这个事件不是实时触发的(当然你也可以专门写一个线程监控) * 为了提高效率,根据WeixinContext中的算法,这里的过期消息会在过期后下一条请求执行之前被清除 */ var messageContext = e.MessageContext as CustomMessageContext; if (messageContext == null) { return;//如果是正常的调用,messageContext不会为null } //TODO:这里根据需要执行消息过期时候的逻辑,下面的代码仅供参考 //Log.InfoFormat("{0}的消息上下文已过期",e.OpenId); } }
上面的CustomMessageContext_MessageContextRemoved()方法會在某個使用者的上下文被清除的時候觸發,其中可以加入自己需要的程式碼。除此之外,您也可以重寫累積中的OnRemoved()等方法,或新增其他的屬性及方法。
更多微信公眾平台開發:解決使用者情境(Session)問題相關文章請關注PHP中文網!