CookieContainer 对象可以用于在 HTTP 请求间传递 Cookie 信息。如果我们需要向 CookieContainer 中添加一条数据,可以采用其 Add 方法:
var cookieContainer = new CookieContainer(); cookieContainer.Add(new Cookie("name", "Soar360", "/", "httpbin.org")); using (var handler = new HttpClientHandler { UseCookies = true, CookieContainer = cookieContainer }) { using (var http = new HttpClient(handler)) { var json = await http.GetStringAsync("https://httpbin.org/cookies"); Console.WriteLine(json); } }
以上代码的输出如下:
{ "cookies": { "name": "Soar360" } }
有时我们需要将 CookieContainer 进行持久化(序列化),将其保存到本地文件或数据库中(比如当 Cookie 包含了对某个资源的访问凭据时)。很长一段时间中,我们只能通过反射来实现该功能:
public static CookieCollection GetAllCookies(CookieContainer cookieJar) { CookieCollection cookieCollection = new CookieCollection(); Hashtable table = (Hashtable) cookieJar.GetType().InvokeMember("m_domainTable", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, cookieJar, new object[] {}); foreach (var tableKey in table.Keys) { String str_tableKey = (string) tableKey; if (str_tableKey[0] == '.') { str_tableKey = str_tableKey.Substring(1); } SortedList list = (SortedList) table[tableKey].GetType().InvokeMember("m_list", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, table[tableKey], new object[] { }); foreach (var listKey in list.Keys) { String url = "https://" + str_tableKey + (string) listKey; cookieCollection.Add(cookieJar.GetCookies(new Uri(url))); } } return cookieCollection; }
测试代码:
CookieContainer cookieContainer = new CookieContainer(); cookieContainer.Add(new Cookie("name1", "value1", "/", ".coderbusy.com")); cookieContainer.Add(new Cookie("name2", "value1", "/path1/", ".coderbusy.com")); cookieContainer.Add(new Cookie("name2", "value1", "/path1/path2/", ".coderbusy.com")); cookieContainer.Add(new Cookie("name1", "value1", "/", ".sum16.com")); cookieContainer.Add(new Cookie("name2", "value1", "/path1/", ".sum16.com")); cookieContainer.Add(new Cookie("name2", "value1", "/path1/path2/", ".sum16.com")); CookieCollection cookies = GetAllCookies(cookieContainer); Console.WriteLine(JsonConvert.SerializeObject(cookies, Newtonsoft.Json.Formatting.Indented));
输出:
[ { "Comment": "", "CommentUri": null, "HttpOnly": false, "Discard": false, "Domain": ".coderbusy.com", "Expired": false, "Expires": "0001-01-01T00:00:00", "Name": "name2", "Path": "/path1/path2/", "Port": "", "Secure": false, "TimeStamp": "2021-11-13T11:31:20.3900972+08:00", "Value": "value1", "Version": 0 },... ]
幸运的是,随着 .NET 6 的正式发布,CookieContainer 类型新增了一个名为 GetAllCookies() 的实例方法,该方法可以获取到 CookieContainer 中所有的 Cookie 信息,并填充 CookieCollection 。所以测试代码可以改为:
CookieContainer cookieContainer = new CookieContainer(); cookieContainer.Add(new Cookie("name1", "value1", "/", ".coderbusy.com")); cookieContainer.Add(new Cookie("name2", "value1", "/path1/", ".coderbusy.com")); cookieContainer.Add(new Cookie("name2", "value1", "/path1/path2/", ".coderbusy.com")); cookieContainer.Add(new Cookie("name1", "value1", "/", ".sum16.com")); cookieContainer.Add(new Cookie("name2", "value1", "/path1/", ".sum16.com")); cookieContainer.Add(new Cookie("name2", "value1", "/path1/path2/", ".sum16.com")); CookieCollection cookies = cookieContainer.GetAllCookies(); Console.WriteLine(JsonConvert.SerializeObject(cookies, Newtonsoft.Json.Formatting.Indented));
该代码会产生同样的输出,但明显更加直观和优雅。