【问题标题】:How can I get all Cookies of a CookieContainer?如何获取 CookieContainer 的所有 Cookie?
【发布时间】:2013-04-05 16:15:54
【问题描述】:

我想使用 Newtonsoft.Json 将 CookieContainer 导出为 JSON,但不幸的是 CookieContainer 没有枚举器或其他东西,所以我无法循环遍历它...

编辑:使用我发布的解决方案,它会是这样的:

private static void Main(string[] args)
{
    CookieContainer cookieContainer = new CookieContainer();
    cookieContainer.Add(new Cookie("name1", "value1", "/", ".testdomain1.com"));
    cookieContainer.Add(new Cookie("name2", "value1", "/path1/", ".testdomain1.com"));
    cookieContainer.Add(new Cookie("name2", "value1", "/path1/path2/", ".testdomain1.com"));
    cookieContainer.Add(new Cookie("name1", "value1", "/", ".testdomain2.com"));
    cookieContainer.Add(new Cookie("name2", "value1", "/path1/", ".testdomain2.com"));
    cookieContainer.Add(new Cookie("name2", "value1", "/path1/path2/", ".testdomain2.com"));

    CookieCollection cookies = GetAllCookies(cookieContainer);

    Console.WriteLine(JsonConvert.SerializeObject(cookies, Formatting.Indented));
    Console.Read();
}

【问题讨论】:

  • 我认为除了使用反射访问 CookieContainer 的私有字段(我不推荐)之外,没有其他方法可以获取所有 cookie。您应该只将 cookie 单独存储,并在需要时将它们放入 CookieContainer 中。

标签: c# .net cookiecontainer


【解决方案1】:

使用反射的解决方案:

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;
}

.NET 6 更新

最后,.NET 6 发布并引入了CookieContainer.GetAllCookies() 方法,该方法提取了CookieCollection - Documentation link

public System.Net.CookieCollection GetAllCookies();

【讨论】:

  • 此代码仅打印 https cookie 并忽略 http。此处改进版本:stackoverflow.com/a/36665793/2010764
  • 我在 http 网站上使用这种方法。它工作正常,它返回所有 http cookie。但是当我将 https 更改为 http 时,它不会打印所有 cookie,只会打印主域上的那些。
【解决方案2】:

此方法将确保获取所有 cookie,无论协议是什么:

public static IEnumerable<Cookie> GetAllCookies(this CookieContainer c)
{
    Hashtable k = (Hashtable)c.GetType().GetField("m_domainTable", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(c);
    foreach (DictionaryEntry element in k)
    {
        SortedList l = (SortedList)element.Value.GetType().GetField("m_list", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(element.Value);
        foreach (var e in l)
        {
            var cl = (CookieCollection)((DictionaryEntry)e).Value;
            foreach (Cookie fc in cl)
            {
                yield return fc;
            }
        }
    }
}

【讨论】:

    【解决方案3】:

    第一个答案不适用于可移植项目。所以这里的选项2,也使用反射

    using System.Linq;
    using System.Collections;
    using System.Reflection;
    using System.Net;
    
        public static CookieCollection GetAllCookies(this CookieContainer container)
        {
            var allCookies = new CookieCollection();
            var domainTableField = container.GetType().GetRuntimeFields().FirstOrDefault(x => x.Name == "m_domainTable");            
            var domains = (IDictionary)domainTableField.GetValue(container);
    
            foreach (var val in domains.Values)
            {
                var type = val.GetType().GetRuntimeFields().First(x => x.Name == "m_list");
                var values = (IDictionary)type.GetValue(val);
                foreach (CookieCollection cookies in values.Values)
                {
                    allCookies.Add(cookies);                    
                }
            }          
            return allCookies;
        }
    

    1) 我也试过了

    var domainTableField = container.GetType().GetRuntimeField("m_domainTable"); 
    

    但它返回 null。

    2) 您可以遍历 domain.Keys 并对所有键使用 container.GetCookies()。但我遇到了问题,因为 GetCookies 需要 Uri 而不是我的所有键都匹配 Uri 模式。

    【讨论】:

      【解决方案4】:

      使用CookieContainer.GetCookies Method

      CookieCollection cookies = cookieContainer.GetCookies(new Uri(url));
      

      url 是您网站的网址。

      就我而言,正如其他答案中所建议的那样,我无法使用反射。但是,我确实知道要查询的网站的 URL。我认为容器不会盲目返回所有 cookie 而是按 URL 返回它们甚至是合乎逻辑的,因为 cookie 始终属于特定 URL,并且不能在与它们关联的域的上下文之外使用。

      【讨论】:

      • 我不知道为什么其他作者需要反射和特殊代码。
      • OP 想要一个所有 cookie 的列表,~不管 url/Uri。这就是区别。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-19
      相关资源
      最近更新 更多