在我们使用HTTP协议进行应用开发的的时候,通常服务器端是不关心请求是从哪个客户到来的,客户端也不并不关心服务器端是通过生成哪个对象处理的这次请求。这就是我们通常说的HTTP无状态请求,从技术的角度上讲是因为:
- 客户端和服务器用TCP Socket通信,服务器将请求结果返回给浏览器后,通常会关闭Socket连接;
- 服务器会在处理页面完毕后销毁页面对象;
- HTTP协议本身就是一个无状态协议;
举个例子,如返回一个Web页面告诉你关于一个商品的信息。可以通过访问一个URL请求服务器。在URL中带有该商品的ID号,服务器用它来产生HTTP页面进行响应。在这个过程中,服务器生成HTML页前可能记住了这个商品的ID以及从数据库取出来的商品名称/价格等信息,并可以通过业务逻辑来决定按哪种风格来显示给客户。一旦服务器完成工作,那些信息就没有任何作用了。
然而我们的业务逻辑通常要求服务端与客户端交互是有状态的。最常见的是很多业务系统要登陆后才能进行操作,而且在整个操作过程中都要携带登陆信息,登陆信息便是会话状态;再比如网购过程中的购物车功能,购物车的内容就是会话状态。这些都是比较典型的基于状态的交互。基于会话的交互关键是如何存储会话状态信息,因为通信的两方只有客户端和服务端,所以会话状态的存储方式也有两种:存储在服务器端和存储在客户端。
客户端存储
客户端存储能常有这样几种方法:数据编码在URL中、WEB表单的隐藏域中、HTML扩展标签及cookie。这里简单说一下具体的操作:
- URL方法:这种方法最常见的就是在分页的时候,URL中带放有页码信息。比如:xx.aspx?page=3 这种方式是最简单有效的方法。但缺点也很明显,安全系数基本为0,而且只适合存储小数据量。
- WEB表单隐藏域: 这种方式和把信息存储在HTML扩展标签中,还有可能在胖客户端的对象结构中其实是差不多一样的。最明显的一个例子就是asp.net中为表单自动生成的那一堆viewdata,它们的原理都是在客户端的WEB页面中放置一些普通用户看不见的东西。所以可以把它们归为一类,至于实际项目中选择使用哪种方式,全凭开发者个人爱好及客户浏览器的支持情况。它的一个典型的实现如下:
1 <input type="hidden" value="你的会话状态信息" id="sessionData"/>
- Cookie:这是一种非常流行的做法。个人认为它流行的原因,很大一部分是因为程序编码者可以直接通过服务端代码实现,而不用像上面那种方式,还要去处理HTML。比如可以直接使用C#实现:
/// <summary> /// 写cookie值 /// </summary> /// <param name="strName">名称</param> /// <param name="strValue">值</param> /// <param name="strValue">过期时间(分钟)</param> public static void WriteCookie(string strName, string strValue, int expires) { HttpCookie cookie = HttpContext.Current.Request.Cookies[strName]; if (cookie == null) { cookie = new HttpCookie(strName); } cookie.Value = strValue; cookie.Expires = DateTime.Now.AddMinutes(expires); HttpContext.Current.Response.AppendCookie(cookie); } /// <summary> /// 读cookie值 /// </summary> /// <param name="strName">名称</param> /// <returns>cookie值</returns> public static string GetCookie(string strName) { if (HttpContext.Current.Request.Cookies[strName] != null) { return HttpContext.Current.Request.Cookies[strName].Value.ToString(); } return ""; }
通过简单的服务端语言就可以实现cookie的读写,而且不需要处理任何数据传递/接收的处理。这样方便是方便,但以前面试过一些人,当问到为什么可以用服务器代码读写存在于客户端电脑上面的cookie时,令我吃惊的是,很多人就只敷衍一句,asp.net的内部机制,我们不用关心。我只想说这些人纯粹就是为了写代码而写代码,完全没有思考过其中的原因。
上图是cookie的标准流程图,在HTTP协议中有一些与cookie有关的扩展头部。帮助我们自动实现cookie信息在客户端与服务端传输。如下:
当在服务端写一个Cookie时,服务器在回传的HTTP中,会添加Set-Cookie把刚刚写入的内容传递到客户计算机中。当客户端再次请求该服务器时,会在请求的HTTP头中添加cookie信息,传递到服务器中,这样才能使服务器代码正常访问到。
服务器存储
在服务器端存储会话信息通常有两个选择:直接存储在内存中和序列化存储在文件系统中(当然,包括存储到数据库中)。对于面向对象的开发语言来说,通常使用静态数据对象(或集合)把状态存储在内存中,这样只想应用程序或服务器不重启,那么状态就可以一直存在,内存状态的典型代表就是asp.net内置的会话状态功能--Session。如果想把状态存储在文件或数据库中,最好的方法就是把会话标识作为关键字,以已序列化的对象为值进行存储。比如我们可以以SessionId作为关键字,把Session的内容序列化并存储到数据库中,这样就可以避免重启造成的Session丢失问题。关于Session的使用可以查看《认识asp.net会话状态》,本文不再详述。
服务端存储并没有什么特殊要讲的地方,因为即不用考虑怎么把内容藏在客户电脑里面,并且让他们不易发现,也不用考虑太多安全问题。
抉择
了解完两边的存储情况后,伴随而来的一个问题就是:我到底该把状态存在哪里?
下表总结了一下,两种存储方式的优点及适用场景:
优点 | 适用场景 | |
服务端存储 | 数据安全性高; 简单,主流服务端开发的支持,大幅减少代码量; |
本人是服务端会话存储的支持者,除下面列举的两种情况外,我基本都会把会话状态存储在服务器。 |
客户端存储 | 用户客户端存储,减少服务器压力; | 1、适合会话状态数据量较少的情况;特别适合存放会话标识,典型的就是Session ID。 2、适合一些临时会话状态,比如购物车。因为用户可能经常取消该会话,并且再也不会用到它。 |
两种模式在实际应用中并不是相互排斥的关系,且不存在绝对的谁好谁坏,可以灵活组合使用。但这样会增加程序编码的复杂度,开发者可以根据自己的情况选择使用。
附:本博客其他精彩内容:http://www.cnblogs.com/yubaolee/p/Catalogue.html