ASP.Net会话

时间:2020-03-06 14:43:11  来源:igfitidea点击:

我想在一系列不同的ASP.Net Web表单中存储用户正在执行的某些操作的"状态"。对于持久化状态,我有哪些选择?每种解决方案的利弊是什么?

我一直在使用Session对象,并使用一些辅助方法来强烈地键入对象:

public static Account GetCurrentAccount(HttpSessionState session)
    {
        return (Account)session[ACCOUNT];
    }

    public static void SetCurrentAccount(Account obj, HttpSessionState session)
    {
        session[ACCOUNT] = obj;
    }

许多消息人士告诉我,"会话是邪恶的",因此,这实际上是这个问题的根本原因。我想知道我们认为"最佳实践"的原因,以及原因。

解决方案

至于"会话是邪恶的"……如果我们使用经典的ASP开发,我必须同意,但是ASP.NET/IIS的工作要好得多。

真正的问题是维持状态的最佳方法是什么。在本例中,当涉及到当前登录的用户时,我们将该对象存储在Session中,因为我们不断地用它的名称,电子邮件地址,授权等来引用该对象。

不需要任何长期持久性的其他信息,我们结合使用cookie和viewstate。

据我所知,Session是存储此信息的预期方式。请记住,默认情况下,会话状态通常存储在进程中。如果我们有多个Web服务器,或者IIS重新启动,则我们将丢失会话状态。可以使用ASP.NET状态服务甚至SQL数据库存储会话来解决此问题。这样可以确保人们重新获得会​​话,即使将他们重新路由到其他Web服务器,或者在回收工作进程的情况下也是如此。

短期信息只需要保留到下一个请求,就可以存储在" ViewState"中。这意味着对象被序列化并存储在发送到浏览器的页面中,然后在发生单击事件或者类似事件时将其发布回服务器。然后,将" ViewState"解码并再次转换为对象,准备进行检索。

会话不是邪恶的,它们在ASP.NET应用程序中起着重要的作用,在用户的"会话"期间提供必须在多个页面之间共享的数据。有一些建议,我会说尽可能使用SQL会话管理,并确保我们在会话集合中使用的对象是"可序列化的"。最佳实践是在绝对需要跨页面共享状态信息时使用会话对象,而在不需要时不使用它。该信息将不会在客户端可用,会话密钥或者保存在cookie中,或者通过查询字符串保存,或者根据配置方式使用其他方法保存,然后会话对象在数据库表中可用(除非我们使用InProc,否则在重新加载站点的过程中会话将有可能被中断,或者在大多数群集环境中几乎变得无用)。

http://www.tigraine.at/2008/07/17/session-handling-in-aspnet/

希望这可以帮助。

会话是邪恶的:不在ASP.NET中,配置正确。是的,尽可能地保持无状态是理想的,但是现实是我们无法从这里到达那里。但是,我们可以使Session以减轻其影响的方式运行-特别是StateServer或者数据库会话。

当我们想存储可以在Web应用程序中全局访问的信息时,一种方法是ThreadStatic属性。这会将"类"的"静态"成员转换为当前线程共享的成员,而不是其他线程共享的成员。 ThreadStatic的优点是我们不必具有可用的Web上下文。例如,如果后端没有引用" System.Web",但也希望在其中共享信息,则可以在" ThreadStatic"属性的每个请求的开始处设置用户的" id",然后在依赖项中引用它,而无需访问Session对象。

因为它是"静态的"但仅对单个线程有效,因此我们确保其他同时访问者不会收到我们的会话。只要我们确保为每个请求都重置该属性,此方法就起作用。这使其成为cookie的理想伴侣。

我认为在这种情况下使用Session对象是可以的,但是我们应该记住,如果长时间没有浏览器活动,Session可能会过期(HttpSessionState.Timeout属性确定会话状态提供程序终止会话的时间),因此返回前检查值是否存在:

public static Account GetCurrentAccount(HttpSessionState session)
{
    if (Session[ACCOUNT]!=null)
        return (Account)Session[ACCOUNT];
    else
        throw new Exception("Can't get current account. Session expired.");
}

我认为"邪恶"来自过度使用会话。如果仅粘贴所有内容(如对所有内容使用全局变量),那么结果将很糟糕,只会一团糟。

会话状态本来就没有邪恶。

有几件事需要牢记,但可能会伤到你:

  • 如果用户按下浏览器后退按钮,则返回上一页,但会话状态不会恢复。因此,CurrentAccount可能与页面上的原始内容不同。
  • IIS可以回收ASP.NET进程。发生这种情况时,下一个请求将开始一个新的过程。如果我们在进程会话状态下使用默认值,它将消失:-(
  • 如果用户一段时间未处于活动状态,则会话也可能会超时,并且结果相同。默认为20分钟,因此可以享用精美的午餐。
  • 使用进程外会话状态要求以会话状态存储的所有对象都可序列化。
  • 如果用户打开第二个浏览器窗口,则他将期望拥有另一个不同的应用程序,但是会话状态很可能在两个之间共享。因此,在一个浏览器窗口中更改CurrentAccount将在另一个浏览器窗口中执行相同的操作。

其声誉不佳的原因之一是,匆忙的开发人员在UI代码中使用字符串文字(而不是像我们这样的帮助器类)作为项目键来过度使用它,并最终导致一大袋无法测试的混杂状态。某种包装是非邪恶会话使用的入门级要求。

临时存储表单数据的两种选择是,首先,将每个表单的信息存储在会话状态变量中,其次,使用URL参数传递表单信息。使用Cookie作为潜在的第三种选择根本不可行,原因很简单,因为许多访问者可能会关闭Cookie(但是,这不会影响会话Cookie)。另外,根据我们问题的性质,我假设我们不希望在完全提交该信息之前将其存储在数据库表中。

使用会话变量是解决此问题的经典方法,但确实存在一些缺陷。其中包括:(1)如果使用Inproc会话管理,则大量数据会耗尽服务器RAM;(2)在服务器场中的多台服务器之间共享会话变量需要另外考虑,(3)专业设计的应用必须防止会话过期(不要仅转换会话变量,如果会话已过期,则不使用它,转换将引发错误)。但是,对于绝大多数应用程序而言,会话变量无疑是必经之路。

另一种方法是在URL中传递每个表单的信息。这种方法的主要问题是,在"传递"信息时必须格外小心。例如,如果要在四个页面中收集信息,则需要在第一页中收集信息,然后将其通过URL传递给第二页,我们必须将其存储在该页面的viewstate中。然后,当调用第三页时,我们将从第二页中收集表单数据以及viewstate变量,并在URL中进行编码,等等。如果我们有五页或者更多页,或者如果访客将在站点周围跳转,真是一团糟。还请记住,所有信息都需要A)序列化为URL安全的字符串,并B)以防止基于URL的简单黑客攻击的方式进行编码(例如,如果我们将价格放入明文中并传递给它,然后,有人可以更改价格)。请注意,我们可以通过创建一种"会话管理器"来减少其中的一些问题,并让它为我们管理URL字符串,但是对于任何给定的链接可能会破坏某人的整个会话的可能性,我们仍然必须非常敏感。管理不当。

最后,我仅将URL变量用于将非常有限的数据从一页传递到下一页(例如,在指向该项目的链接中编码的项目ID)。

那么,让我们假设我们确实可以使用内置的Sessions功能来管理用户的数据。为什么有人会告诉我们"会话是邪恶的"?嗯,除了上面介绍的内存负载,服务器场和过期注意事项之外,对Session变量的主要批评是它们实际上是无类型变量。

幸运的是,谨慎使用Session变量可以避免内存问题(无论如何,大项目都应保留在数据库中),如果我们运行的站点足够大,需要服务器场,则可以使用许多机制来共享内置在ASP中的状态.NET(提示:我们将不使用inproc存储)。

为了基本上避免Session其余所有缺点,我建议实现一个对象来保存会话数据以及一些简单的Session对象管理功能。然后将它们构建到Page类的后代中,并将此后代Page类用于所有页面。然后,通过页面类作为一组强类型值访问Session数据就很简单了。请注意,我们对象的字段将为我们提供一种以强类型方式访问每个"会话变量"的方式(例如,每个变量一个字段)。

让我知道这对我们来说是一项简单的任务,还是我们想要一些示例代码!

除非将其清除,否则我们在会话对象中放置的所有内容都会在会话期间停留在那里。使用inproc和stateserver存储的内存管理不善,将迫使我们提前进行横向扩展。在会话中仅存储会话/用户的ID,并使用帮助程序类按需将所需的内容加载到缓存对象中。这样,我们可以根据我们使用该数据的频率来微调其使用寿命。下一个版本的asp.net可能具有分布式缓存(谣言)。