从这篇文章中咱们已经了解了微信公众平台消息传递的方式,这种方式有一个先天的缺陷:不一样用户的请求都来自同一个微信服务器,这使得常规的Session没法使用(始终面对同一个请求对象,何况还有对方服务器Cookie是否能保存的问题)。html
这就要求咱们本身创建一套独立的对话上下文请求机制。git
上一篇《Senparc.Weixin.MP SDK 微信公众平台开发教程(六):了解MessageHandler》中咱们了解到,Senparc.Weixin.MP SDK提供了一套很是便捷的消息处理机制,这套机制上面,咱们有增长了一个叫MessageContext的上下文处理机制,下面咱们就来讲一说这个功能的使用(参考开源项目Wiki中的《用户上下文WeixinContext和MessageContext》一文)。github
更多实际应用的代码能够参考开源项目中的Demo:https://github.com/JeffreySu/WeiXinMPSDK,此外您也能够关注下方的微信二维码,不断发送文字及各类信息进行测试(发送文字的时候才能看到完整结果,期间能够发送其余任意类型)。web
因为微信公众平台的特殊机制,全部的信息都由微信服务器转发而来,所以服务器是没法使用Session对用户会话的上下文进行管理的。算法
为此Senparc.WeiXin.MP SDK增长了上下文的模块,并集成到了MessageHandler中。小程序
WeixinContext是全部单个用户上下文(MessageContext)实体的容器(暂且称为全局上下文)。WeixinContext自己不是静态类,意味着您能够在同一个应用中建立多个上下文实体。缓存
同时,一个静态的WeixinContext实例被放入到MessageHandler<TM>
中,所以全部项目中由MessageHandler<TM>
派生的子类中的WeixinContext是惟一的、全局的(注:TM为实现IMessageContext的类,包括SDK中已经提供的MessageContext)。服务器
所以咱们在任何一个实现了MessageHandler<TM>
的实例中(好比叫MyMessageHandler),均可以访问到一个类型和名称都叫WeixinContext的对象。微信
WeixinContext用于保存所用户的上下文(MessageContext),而且提供了一系列的方法,主要方法包括:websocket
/// <summary> /// 重置全部上下文参数,全部记录将被清空 /// </summary> public void Restore() { ... } /// <summary> /// 获取MessageContext,若是不存在,返回null /// 这个方法的更重要意义在于操做TM队列,及时移除过时信息,并将最新活动的对象移到尾部 /// </summary> /// <param name="userName">用户名(OpenId)</param> /// <returns></returns> private TM GetMessageContext(string userName) { ... } /// <summary> /// 获取MessageContext /// </summary> /// <param name="userName">用户名(OpenId)</param> /// <param name="createIfNotExists">True:若是用户不存在,则建立一个实例,并返回这个最新的实例 /// False:用户储存在,则返回null</param> /// <returns></returns> private TM GetMessageContext(string userName, bool createIfNotExists) { ... } /// <summary> /// 获取MessageContext,若是不存在,使用requestMessage信息初始化一个,并返回原始实例 /// </summary> /// <returns></returns> public TM GetMessageContext(IRequestMessageBase requestMessage) { ... } /// <summary> /// 获取MessageContext,若是不存在,使用requestMessage信息初始化一个,并返回原始实例 /// </summary> /// <returns></returns> public TM GetMessageContext(IResponseMessageBase responseMessage) { ... } /// <summary> /// 记录请求信息 /// </summary> /// <param name="requestMessage">请求信息</param> public void InsertMessage(IRequestMessageBase requestMessage) { ... } /// <summary> /// 记录响应信息 /// </summary> /// <param name="responseMessage">响应信息</param> public void InsertMessage(IResponseMessageBase responseMessage) { ... } /// <summary> /// 获取最新一条请求数据,若是不存在,则返回Null /// </summary> /// <param name="userName">用户名(OpenId)</param> /// <returns></returns> public IRequestMessageBase GetLastRequestMessage(string userName) { ... } /// <summary> /// 获取最新一条响应数据,若是不存在,则返回Null /// </summary> /// <param name="userName">用户名(OpenId)</param> /// <returns></returns> public IResponseMessageBase GetLastResponseMessage(string userName) { ... }
WeixinContext中有两个用于储存用户上下文的对象:MessageCollection及MessageQueue。
这两个对象中的元素集合是重合的,可是MessageQueue对元素进行了排序,以便及时处理掉顶部过时的上下文。
ExpireMinutes用于定义上下文时间有效期,默认为90分钟。能够在程序的任何地方设置设个参数,且当即生效。
PS:MessageQueue中删除过时数据的逻辑以极高的效率运做,常规开发时无需考虑CPU占用及对象冲突的问题(额外校验时间是否超时)。
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的做用。
上述MessageContext<TRequest,TResponse>类已经提供了比较完备的经常使用的消息处理的方法,能够直接使用。若是有更加特殊的需求,须要自定义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()等方法,或者添加其余的属性及方法。
地址:http://www.cnblogs.com/szw/archive/2013/05/14/weixin-course-index.html