这篇文章中我们已经了解了微信公众平台消息传递的方式,这种方式有一个先天的缺陷:不同用户的请求都来自同一个微信服务器,这使得常规的Session无法使用(始终面对同一个请求对象,况且还有对方服务器Cookie是否能保存的问题)。

这就要求我们自己建立一套独立的对话上下文请求机制。

上一篇《Senparc.Weixin.MP SDK 微信公众平台开发教程(六):了解MessageHandler》中我们了解到,Senparc.Weixin.MP SDK提供了一套非常便捷的消息处理机制,这套机制上面,我们有增加了一个叫MessageContext的上下文处理机制,下面我们就来说一说这个功能的使用(参考开源项目Wiki中的《用户上下文WeixinContext和MessageContext》一文)。

更多实际应用的代码可以参考开源项目中的Demo:https://github.com/JeffreySu/WeiXinMPSDK,此外您也可以关注下方的微信二维码,不断发送文字及各种信息进行测试(发送文字的时候才能看到完整结果,期间可以发送其他任意类型)。


概述

由于微信公众平台的特殊机制,所有的信息都由微信服务器转发而来,因此服务器是无法使用Session对用户会话的上下文进行管理的。

为此Senparc.WeiXin.MP SDK增加了上下文的模块,并集成到了MessageHandler中。

WeixinContext

WeixinContext是所有单个用户上下文(MessageContext)实体的容器(暂且称为全局上下文)。WeixinContext本身不是静态类,意味着您可以在同一个应用中创建多个上下文实体。

同时,一个静态的WeixinContext实例被放入到MessageHandler<TM>中,因此所有项目中由MessageHandler<TM>派生的子类中的WeixinContext是唯一的、全局的(注:TM为实现IMessageContext的类,包括SDK中已经提供的MessageContext)。

因此我们在任何一个实现了MessageHandler<TM>的实例中(比如叫MyMessageHandler),都可以访问到一个类型和名称都叫WeixinContext的对象。

WeixinContext用于保存所用户的上下文(MessageContext),并且提供了一系列的方法,主要方法包括:

      /// <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

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没有对它进行任何引用,完全由开发者决定里面的内容(比如用户执行到哪一步、或某个比较重要的位置信息等等),类似于Session的作用。

本人参考该教程编写了一个微信公众平台服务平台小云吞微管理,网址: http://www.xiaoyuntun.com

转载收藏之用 - 微信公众平台开发教程(七):解决用户上下文(Session)问题的更多相关文章

  1. 转载收藏之用 - 微信公众平台开发教程(六):了解MessageHandler

    上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK>我们讲述了如何使用Senparc.Weixin.MP SDK ...

  2. 转载收藏之用 - 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK

    Senparc.Weixin.MP SDK已经涵盖了微信5.0的所有公共API,以及2013年10月29日升级之后大部分实用的接口. 整个项目的源代码以及已经编译好的程序集可以在这个项目中获取到:ht ...

  3. 转载收藏之用 - 微信公众平台开发教程(四):Hello World

    这一篇文章其实可以写在很前面,不过我还是希望开发者们尽多地了解清楚原理之后再下手. 通过上一篇Senparc.Weixin.MP SDK 微信公众平台开发教程(三):微信公众平台开发验证,我们已经使微 ...

  4. Senparc.Weixin.MP SDK 微信公众平台开发教程(十八):Web代理功能

    在Senparc.Weixin.dll v4.5.7版本开始,我们提供了Web代理功能,以方便在受限制的局域网内的应用可以顺利调用接口. 有关的修改都在Senparc.Weixin/Utilities ...

  5. Senparc.Weixin.MP SDK 微信公众平台开发教程(十七):个性化菜单接口说明

    前不久微信上线了个性化菜单接口,Senparc.Weixin SDK也已经同步更新. 本次更新升级Senparc.Weixin.MP版本到v13.5.2,依赖Senparc.Weixin版本4.5.4 ...

  6. Senparc.Weixin.MP SDK 微信公众平台开发教程(三):微信公众平台开发验证

    要对接微信公众平台的"开发模式",即对接到自己的网站程序,必须在注册成功之后(见Senparc.Weixin.MP SDK 微信公众平台开发教程(一):微信公众平台注册),等待官方 ...

  7. Senparc.Weixin.MP SDK 微信公众平台开发教程(四):Hello World

    =============  以下写于2013-07-20 ============= 这一篇文章其实可以写在很前面,不过我还是希望开发者们尽多地了解清楚原理之后再下手. 通过上一篇Senparc.W ...

  8. Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK

    Senparc.Weixin.MP SDK已经涵盖了微信6.x的所有公共API. 整个项目的源代码以及已经编译好的程序集可以在这个项目中获取到:https://github.com/JeffreySu ...

  9. Senparc.Weixin.MP SDK 微信公众平台开发教程(六):了解MessageHandler

    上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK>我们讲述了如何使用Senparc.Weixin.MP SDK ...

随机推荐

  1. 十分钟让你的ASP.NET MVC网站变成PHP

    昨天从网上看来的,某个人发的方法: 第一:修改一下路由: 把RouteConfig中的: routes.MapRoute( name: "Default", url: " ...

  2. Android数据存储

    SharedPreferences----------->for primitive data Internal Storage-------------->for private dat ...

  3. 来,试试PERL

    试试,看看能否真的替代AWK,SED这些的... #!/usr/bin/perl print "hello, world!\n"; $line = <STDIN>; i ...

  4. 2015第24周三Spring事务3

    在一个典型的事务处理场景中,有以下几个参与者: Resource Manager(RM) ResourceManager简称RM,它负责存储并管理系统数据资源的状态,比如数据库服务器,JMS消息服务器 ...

  5. cf492A Vanya and Cubes

    A. Vanya and Cubes time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  6. 在 Java 应用程序中使用 Elasticsearch

    如果您使用过 Apache Lucene 或 Apache Solr,就会知道它们的使用体验非常有趣.尤其在您需要扩展基于 Lucene 或 Solr 的解决方案时,您就会了解 Elasticsear ...

  7. 浅谈c语言代码段 数据段 bss段

    代码段.数据段.bss段 (1)编译器在编译程序的时候,将程序中的所有的元素分成了一些组成部分,各部分构成一个段,所以说段是可执行程序的组成部分. (2)代码段:代码段就是程序中的可执行部分,直观理解 ...

  8. Android——ViewPager多页面滑动切换以及动画效果

    一.首先,我们来看一下效果图,这是新浪微博的Tab滑动效果.我们可以手势滑动,也可以点击上面的头标进行切换.与此同方式,白色横条会移动到相应的页卡头标下.这是一个动画效果,白条是缓慢滑动过去的.好了, ...

  9. cobol语言基础培训教程

    COBOL 是Common Business Oriented Language 的缩写.它不仅是商业数据处理的理想语言,而且广泛用于数据管理领域,因此COBOL 语言也被称为”用于管理的语言”. 一 ...

  10. android layout属性介绍

    android:id 为控件指定对应的ID android:text 指定控件其中显示的文字,须要注意的是,这里尽量使用strings.xml文件其中的字符串 android:gravity 指定Vi ...