Home  >  Article  >  WeChat Applet  >  WeChat public platform development: solving user context (Session) issues

WeChat public platform development: solving user context (Session) issues

高洛峰
高洛峰Original
2017-02-27 13:08:183074browse

Overview

Due to the special mechanism of the WeChat public platform, all information is forwarded by the WeChat server, so the server cannot use Session to manage the context of the user session.

For this reason, Senparc.WeiXin.MP SDK adds a context module and integrates it into MessageHandler.

WeixinContext

WeixinContext is a container for all single user context (MessageContext) entities (tentatively called the global context). WeixinContext itself is not a static class, which means you can create multiple context entities in the same application.

At the same time, a static WeixinContext instance is put into MessageHandler68b68cd4f5c098c0c63d969fd4792098, so the WeixinContext in all subclasses derived from MessageHandler68b68cd4f5c098c0c63d969fd4792098 in all projects is unique and global (Note: TM is Classes that implement IMessageContext, including MessageContext already provided in the SDK).

So in any instance that implements MessageHandler68b68cd4f5c098c0c63d969fd4792098 (such as MyMessageHandler), we can access an object whose type and name are WeixinContext.

WeixinContext is used to save the user's context (MessageContext) and provides a series of methods. The main methods include:

/// 631fb227578dfffda61e1fa4d04b7d25
/// Reset all context parameters, all records will be cleared
/// 039f3e95db2a684c7b74365531eb6044
public void Restore()
{
...
}

/// & lt; Summary & GT;
/// Get MESSAGECONTEXT, if it does not exist, return null
/// It is to operate the TM queue, remove expired information in time, and move the latest active objects to the end
/// 039f3e95db2a684c7b74365531eb6044
/// 07767e6fd826e8d979ffc3a9d858631aUsername ( OpenId)8bb7487ae6a16a43571bc14c7fcf93c2
                                                                                                                                                 ## /// 631fb227578dfffda61e1fa4d04b7d25
/// Get MessageContext
/// 039f3e95db2a684c7b74365531eb6044
/// 07767e6fd826e8d979ffc3a9d858631aUsername (OpenId)< ;/param>
/// e63a580229b63ffa691b29f71803cad5True: If the user does not exist, create an instance and return the latest instance
/// False: The user is stored in, Then return null8bb7487ae6a16a43571bc14c7fcf93c2
                                                                                                                                                                                                                      

## /// 631fb227578dfffda61e1fa4d04b7d25
/// Get the MessageContext. If it does not exist, initialize one using the requestMessage information and return the original instance
/// 039f3e95db2a684c7b74365531eb6044
/// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3
public TM GetMessageContext(IRequestMessageBase requestMessage)
{
...
}

/// /// Get the MessageContext. If it does not exist, initialize one using requestMessage information and return the original instance
/// 039f3e95db2a684c7b74365531eb6044
/// 2363942ed0d6cd3e85bae1dffa568116f7735d9f6a7af371769ab5c16d23b2f3
public TM GetMessageContext(IResponseMessageBase responseMessage)
{
        ...
    }

    /// 631fb227578dfffda61e1fa4d04b7d25
                                                                            to    // 039f3e95db2a684c7b74365531eb6044
/// 2545ceab610d17bcaea08b4927a7eab9Request information8bb7487ae6a16a43571bc14c7fcf93c2
public void InsertMessage(IRequestMessageBase requestMessage)
{
...
}

                                                                                                                                                                                 ">Response message8bb7487ae6a16a43571bc14c7fcf93c2
public void InsertMessage(IResponseMessageBase responseMessage)
{
...
}

/// 631fb227578dfffda61e1fa4d04b7d25
/// Get the latest request data, if it does not exist, return Null
/// 039f3e95db2a684c7b74365531eb6044
/// 07767e6fd826e8d979ffc3a9d858631aUsername (OpenId)ea958c64f9fd88e22a02074fe798a5bc
                                                                                                                                                                                                      / 631fb227578dfffda61e1fa4d04b7d25
/// Get the latest response data, if it does not exist, return Null
/// 039f3e95db2a684c7b74365531eb6044
///                                                                                                                                                                                                                 

WeixinContext has two objects used to store user context: MessageCollection and MessageQueue.

The element collections in these two objects overlap, but the MessageQueue sorts the elements so that the top expired context can be processed in a timely manner.

ExpireMinutes is used to define the context time validity period, the default is 90 minutes. You can set a parameter anywhere in the program and it will take effect immediately.

PS: The logic of deleting expired data in MessageQueue operates with extremely high efficiency. There is no need to consider CPU usage and object conflicts (whether the additional verification time times out) during regular development.

MessageContext

MessageContext is used to save the context information of a single user and is stored in the MessageCollection and MessageQueue objects of WeixinContext. IMessageContext is defined as follows:

/// <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();
}

You can create your own class according to your own needs, implement this interface, and be used by WeixinContext. Of course, if your requirements are not so special and you are lazy, the SDK provides a default MessageContext implementation:

/// <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);
    }
}

The above code is easy to understand based on the comments. What needs to be explained is StorageData. This is a container used to store any data related to the user context. WeixinContext and IMessageContext do not have any reference to it. It is entirely up to the developer to determine the content (such as which step the user has taken, or some important location information, etc. etc.), similar to the role of Session.

The above MessageContext25193dfd7deff13abfdc5aafbe503ab2 class has provided relatively complete common message processing methods and can be used directly. If you have more special needs and need to customize MessageContext, it is recommended to use this class as the base class to make necessary rewrites. For example, the following is a custom context class:

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);
    }
}

The above CustomMessageContext_MessageContextRemoved() method will be Triggered when a user's context is cleared, you can add the code you need. In addition, you can also override methods such as OnRemoved() in accumulation, or add other attributes and methods.

For more articles related to WeChat public platform development: solving user context (Session) issues, please pay attention to the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn