-
Notifications
You must be signed in to change notification settings - Fork 4.4k
用户上下文WeixinContext和MessageContext
由于微信公众平台的特殊机制,所有的信息都由微信服务器转发而来,因此服务器是无法使用Session对用户会话的上下文进行管理的。
为此SDK增加了上下文的模块,并集成到了MessageHandler中。
WeixinContext是全局的上下文实体。WeixinContext本身不是静态类,意味着您可以在同一个应用中创建多个上下文实体。
同时,一个静态的WeixinContext实例被放入到MessageHandler中,因此所有项目中由MessageHandler派生的子类中的WeixinContext是唯一的、全局的(注:TM为实现IMessageContext的类,包括WeixinContext)。
因此我们在任何一个实现了MessageHandler的实例中(比如叫MyMessageHandler),都可以访问到一个类型和名称都叫WeixinContext的对象。
WeixinContext用于保存所用户的上下文(MessageContext),并且提供了一系列的方法,主要方法包括:
/// <summary>
/// 重置所有上下文参数,所有记录将被清空
/// </summary>
public void Restore()
{
MessageCollection = new Dictionary<string, TM>(StringComparer.OrdinalIgnoreCase);
MessageQueue = new List<TM>();
ExpireMinutes = 90;
}
/// <summary>
/// 获取MessageContext,如果不存在,返回null
/// 这个方法的更重要意义在于操作TM队列,及时移除过期信息,并将最新活动的对象移到尾部
/// </summary>
/// <param name="userName">用户名(OpenId)</param>
/// <returns></returns>
private TM GetMessageContext(string userName)
{
//检查并移除过期记录,为了尽量节约资源,这里暂不使用独立线程轮询
while (MessageQueue.Count > 0)
{
var firstMessageContext = MessageQueue[0];
var timeSpan = DateTime.Now - firstMessageContext.LastActiveTime;
if (timeSpan.TotalMinutes >= ExpireMinutes)
{
MessageQueue.RemoveAt(0);//从队列中移除过期对象
MessageCollection.Remove(firstMessageContext.UserName);//从集合中删除过期对象
}
else
{
break;
}
}
/*
* 全局只有在这里用到MessageCollection.ContainsKey
* 充分分离MessageCollection内部操作,
* 为以后变化或扩展MessageCollection留余地
*/
if (!MessageCollection.ContainsKey(userName))
{
return null;
}
return MessageCollection[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)
{
var weixinContext = GetMessageContext(userName);
if (weixinContext == null)
{
if (createIfNotExists)
{
//全局只在这一个地方使用MessageCollection.Add
MessageCollection[userName] = new TM() { UserName = userName };
weixinContext = GetMessageContext(userName);
//插入列队
MessageQueue.Add(weixinContext); //最新的排到末尾
}
else
{
return null;
}
}
return weixinContext;
}
/// <summary>
/// 获取MessageContext,如果不存在,使用requestMessage信息初始化一个,并返回原始实例
/// </summary>
/// <returns></returns>
public TM GetMessageContext(IRequestMessageBase requestMessage)
{
lock (WeixinContextGlobal.Lock)
{
return GetMessageContext(requestMessage.FromUserName, true);
}
}
/// <summary>
/// 获取MessageContext,如果不存在,使用requestMessage信息初始化一个,并返回原始实例
/// </summary>
/// <returns></returns>
public TM GetMessageContext(IResponseMessageBase responseMessage)
{
lock (WeixinContextGlobal.Lock)
{
return GetMessageContext(responseMessage.ToUserName, true);
}
}
/// <summary>
/// 记录请求信息
/// </summary>
/// <param name="requestMessage">请求信息</param>
public void InsertMessage(IRequestMessageBase requestMessage)
{
lock (WeixinContextGlobal.Lock)
{
var userName = requestMessage.FromUserName;
var messageContext = GetMessageContext(userName, true);
if (messageContext.RequestMessages.Count > 0)
{
//如果不是新建的对象,把当前对象移到队列尾部(新对象已经在底部)
var messageContextInQueue =
MessageQueue.FindIndex(z => z.UserName == userName);
if (messageContextInQueue >= 0)
{
MessageQueue.RemoveAt(messageContextInQueue); //移除当前对象
MessageQueue.Add(messageContext); //插入到末尾
}
}
messageContext.LastActiveTime = DateTime.Now;//记录请求时间
messageContext.RequestMessages.Add(requestMessage);//录入消息
}
}
/// <summary>
/// 记录响应信息
/// </summary>
/// <param name="responseMessage">响应信息</param>
public void InsertMessage(IResponseMessageBase responseMessage)
{
lock (WeixinContextGlobal.Lock)
{
var messageContext = GetMessageContext(responseMessage.ToUserName, true);
messageContext.ResponseMessages.Add(responseMessage);
}
}
/// <summary>
/// 获取最新一条请求数据,如果不存在,则返回Null
/// </summary>
/// <param name="userName">用户名(OpenId)</param>
/// <returns></returns>
public IRequestMessageBase GetLastRequestMessage(string userName)
{
lock (WeixinContextGlobal.Lock)
{
var messageContext = GetMessageContext(userName, true);
return messageContext.RequestMessages.LastOrDefault();
}
}
/// <summary>
/// 获取最新一条响应数据,如果不存在,则返回Null
/// </summary>
/// <param name="userName">用户名(OpenId)</param>
/// <returns></returns>
public IResponseMessageBase GetLastResponseMessage(string userName)
{
lock (WeixinContextGlobal.Lock)
{
var messageContext = GetMessageContext(userName, true);
return messageContext.ResponseMessages.LastOrDefault();
}
}
WeixinContext中有两个用于储存用户上下文的对象:MessageCollection及MessageQueue。
这两个对象中的元素集合是重合的,但是MessageQueue对元素进行了排序,以便及时处理掉顶部过期的上下文。
ExpireMinutes用于定义上下文时间有效期,默认为90分钟。可以在程序的任何地方设置设个参数,且立即生效。
MessageContext用于保存单个用户的上下文信息,被储存在WeixinContext的MessageCollection及MessageQueue对象中。 IMessageContext定义如下:
public interface IMessageContext
{
/// <summary>
/// 用户名(OpenID)
/// </summary>
string UserName { get; set; }
/// <summary>
/// 最后一次活动时间(用户主动发送Resquest请求的时间)
/// </summary>
DateTime LastActiveTime { get; set; }
/// <summary>
/// 接收消息记录
/// </summary>
List<IRequestMessageBase> RequestMessages { get; set; }
/// <summary>
/// 响应消息记录
/// </summary>
List<IResponseMessageBase> ResponseMessages { get; set; }
/// <summary>
/// 临时储存数据,如用户状态等,出于保持.net 3.5版本,这里暂不使用dynamic
/// </summary>
object StorageData { get; set; }
}
您可以根据自己的需要创建自己的类,实现这个接口,并且被WeixinContext使用。当然如果你的要求不是那么特殊,而且你比较懒的话,SDK提供了一个默认的MessageContext实现:
/// <summary>
/// 微信消息上下文(单个用户)
/// </summary>
public class MessageContext : IMessageContext
{
public string UserName { get; set; }
public DateTime LastActiveTime { get; set; }
public List<IRequestMessageBase> RequestMessages { get; set; }
public List<IResponseMessageBase> ResponseMessages { get; set; }
public object StorageData { get; set; }
public MessageContext()
{
/*
* 注意:即使使用其他类实现IMessageContext,
* 也务必在这里进行下面的初始化,尤其是设置当前时间,
* 这个时间关系到及时从缓存中移除过期的消息,节约内存使用
*/
RequestMessages = new List<IRequestMessageBase>();
ResponseMessages = new List<IResponseMessageBase>();
LastActiveTime = DateTime.Now;
}
}
上面的代码根据注释很好理解,需要说明一下的是StorageData。这是一个用于储存任何和用户上下文有关数据的容器,WeixinContext和IMessageContext没有对它进行任何引用,完全由开发者决定里面的内容(比如用户执行到哪一步、或某个比较重要的位置信息等等)。