软件下载 | 资讯教程 | 最近更新 | 下载排行 | 一键转帖 | 发布投稿
您的位置:最火下载站 > 网络编程 > ASP.NET > 细说 Cookie

细说 Cookie

Cookie虽然是个很简单的东西,但它又是WEB开发中一个很重要的客户端数据来源,而且它可以实现扩展性很好的会话状态,所以我认为每个WEB开发人员都有必要对它有个清晰的认识。本文将对Cookie这个话题做一个全面的描述,也算是本人对Cookie的认识总结。

Cookie 概述

Cookie是什么? Cookie 是一小段文本信息,伴随着用户请求和页面在 Web 服务器和浏览器之间传递。Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息。

为什么需要Cookie? 因为HTTP协议是无状态的,对于一个浏览器发出的多次请求,WEB服务器无法区分是不是来源于同一个浏览器。所以,需要额外的数据用于维护会话。 Cookie 正是这样的一段随HTTP请求一起被传递的额外数据。

Cookie能做什么? Cookie只是一段文本,所以它只能保存字符串。而且浏览器对它有大小限制以及它会随着每次请求被发送到服务器,所以应该保证它不要太大。 Cookie的内容也是明文保存的,有些浏览器提供界面修改,所以,不适合保存重要的或者涉及隐私的内容。

Cookie 的限制。 大多数浏览器支持最大为 4096 字节的 Cookie。由于这限制了 Cookie 的大小,最好用 Cookie 来存储少量数据,或者存储用户 ID 之类的标识符。用户 ID 随后便可用于标识用户,以及从数据库或其他数据源中读取用户信息。浏览器还限制站点可以在用户计算机上存储的 Cookie 的数量。大多数浏览器只允许每个站点存储 20 个 Cookie;如果试图存储更多 Cookie,则最旧的 Cookie 便会被丢弃。有些浏览器还会对它们将接受的来自所有站点的 Cookie 总数作出绝对限制,通常为 300 个。

通过前面的内容,我们了解到Cookie是用于维持服务端会话状态的,通常由服务端写入,在后续请求中,供服务端读取。下面本文将按这个过程看看Cookie是如何从服务端写入,最后如何传到服务端以及如何读取的。

Cookie的写、读过程

在Asp.net中,读写Cookie是通过使用HttpCookie类来完成的,它的定义如下:

public sealed class HttpCookie
{
    // 获取或设置将此 Cookie 与其关联的域。默认值为当前域。
    public string Domain { get; set; }
    // 获取或设置此 Cookie 的过期日期和时间(在客户端)。
    public DateTime Expires { get; set; }
    // 获取一个值,通过该值指示 Cookie 是否具有子键。
    public bool HasKeys { get; }
    // 获取或设置一个值,该值指定 Cookie 是否可通过客户端脚本访问。
    // 如果 Cookie 具有 HttpOnly 属性且不能通过客户端脚本访问,则为 true;否则为 false。默认为 false。
    public bool HttpOnly { get; set; }
    // 获取或设置 Cookie 的名称。
    public string Name { get; set; }
    // 获取或设置要与当前 Cookie 一起传输的虚拟路径。默认值为当前请求的路径。
    public string Path { get; set; }
    // 获取或设置一个值,该值指示是否使用安全套接字层 (SSL)(即仅通过 HTTPS)传输 Cookie。
    public bool Secure { get; set; }
    // 获取或设置单个 Cookie 值。默认值为空引用。
    public string Value { get; set; }
    // 获取单个 Cookie 对象所包含的键值对的集合。
    public NameValueCollection Values { get; }
    // 获取 System.Web.HttpCookie.Values 属性的快捷方式。
    public string this[string key] { get; set; }
}

Cookie写入浏览器的过程:我们可以使用如下代码在Asp.net项目中写一个Cookie 并发送到客户端的浏览器(为了简单我没有设置其它属性)。

HttpCookie cookie = new HttpCookie("MyCookieName", "string value");
Response.Cookies.Add(cookie);

我想很多人都写过类似的代码,但是,大家有没有想过:Cookie最后是如何发送到客户端的呢?我们打开Fiddler来看一下吧。

从上图,您应该能发现,我们在服务端写的Cookie,最后其实是通过HTTP的响应头这种途径发送到客户端的。每一个写入动作,都会产生一个【Set-Cookie】的响应头。
浏览器正是在每次获取请求的响应后,检查这些头来接收Cookie的。

Asp.net获取Cookie的过程:我们可以使用如下代码在Asp.net项目中读取一个Cookie

HttpCookie cookie = Request.Cookies["MyCookieName"];
if( cookie != null )
    labCookie1.Text = cookie.Value;
else
    labCookie1.Text = "未定义";

代码同样也很简单,还是类似的问题:大家有没有想过,Cookie是如何传到服务端的呢?我们还是继续使用Fiddler来寻找答案吧。

从图片中,我们可以发现,Cookie是放在请求头中,发送到服务端的。如果你一直刷新页面,就能发现, 每次HTTP请求,Cookie都会被发送。当然了,浏览器也不是发送它所接收到的所有Cookie,它会检查当前要请求的域名以及目录,只要这二项目与Cookie对应的Domain和Path匹配,才会发送。对于Domain则是按照尾部匹配的原则进行的。
所以,我在访问 www.cnblogs.com 时,浏览器并不会将我在浏览 www.163.com 所接收到的 Cookie 发出去。

删除Cookie:其实就是在写Cookie时,设置Expires为一个【早于现在时间的时间】。也就是:设置此Cookie已经过期,浏览器接收到这个Cookie时,便会删除它们。

HttpCookie cookie = new HttpCookie("MyCookieName", null);
cookie.Expires = new DateTime(1900, 1, 1);
Response.Cookies.Add(cookie);

使用Cookie保存复杂对象

前面的示例代码大致演示了Cookie的读写操作。不过,我们平时可能希望将更复杂的【自定义类型】通过Cookie来保存,那么又该如何操作呢?对于这个问题,我们定义一个类型来看看如何处理。

public class DisplaySettings 
{
    public int Style;

    public int Size;
    
    public override string ToString()
    {
        return string.Format("Style = {0}, Size = {1}", this.Style, this.Size);
    }    
}

上面的代码,我定义一个类型,用于保存用户在浏览页面时的显示设置。接下来,我将介绍二种方法在Cookie中保存并读取它们。

方法-1,经典做法。(注意前面给出的HttpCookie定义代码中的最后二个成员)

private void WriteCookie_2a()
{
    DisplaySettings setting = new DisplaySettings { Style = 1, Size = 24 };

    HttpCookie cookie = new HttpCookie("DisplaySettings1");
    cookie["Style"] = setting.Style.ToString();
    cookie["Size"] = setting.Size.ToString();

    Response.Cookies.Add(cookie);
}

private void ReadCookie_2a()
{
    HttpCookie cookie = Request.Cookies["DisplaySettings1"];
    if( cookie == null )
        labDisplaySettings1.Text = "未定义";
    else {
        DisplaySettings setting = new DisplaySettings();
        setting.Style = cookie["Style"].TryToInt();
        setting.Size = cookie["Size"].TryToInt();
        labDisplaySettings1.Text = setting.ToString();
    }
}

方法-2,将对象JSON序列化为字符串。

private void WriteCookie_2b()
{
    DisplaySettings setting = new DisplaySettings { Style = 2, Size = 48 };

    HttpCookie cookie = new HttpCookie("DisplaySettings2", setting.ToJson());
    Response.Cookies.Add(cookie);
}

private void ReadCookie_2b()
{
    HttpCookie cookie = Request.Cookies["DisplaySettings2"];
    if( cookie == null )
        labDisplaySettings2.Text = "未定义";
    else {
        DisplaySettings setting = cookie.Value.FromJson<DisplaySettings>();
        labDisplaySettings2.Text = setting.ToString();
    }
}

这段代码使用了我定义的二个扩展方法。

/// <summary>
/// 将一个对象序列化成 JSON 格式字符串
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToJson(this object obj)
{
    if( obj == null )
        return string.Empty;

    JavaScriptSerializer jss = new JavaScriptSerializer();
    return jss.Serialize(obj);
}

/// <summary>
/// 从JSON字符串中反序列化对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="cookie"></param>
/// <returns></returns>
public static T FromJson<T>(this string cookie)
{
    if( string.IsNullOrEmpty(cookie) )
        return default(T);

    JavaScriptSerializer jss = new JavaScriptSerializer();
    return jss.Deserialize<T>(cookie);
}

对于这二种方法,我个人更喜欢后者,因为它具有更好扩展性:如果类型增加了成员,不需要修改读写Cookie的代码。
不过,这种方式产生的有些字符,比如【双引号】,极少数浏览器(Opera)不支持,所以需要做UrlEncode或者Base64编码处理。
同理,对于第一种方法,遇到Value有【双引号】时,我们同样需要做UrlEncode或者Base64编码处理。

相关阅读
网友评论
栏目导航
推荐软件