如何获取CookieContainer中的所有Cookie?

29
我想使用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();
}

1
我认为除了使用反射访问CookieContainer的私有字段(我不建议这样做),没有其他方法可以获取所有的cookie。你应该将cookie单独存储,并在需要时将它们放入CookieContainer中。 - Will
4个回答

26

使用反射的解决方案:

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 - 文档链接

public System.Net.CookieCollection GetAllCookies();

这段代码只打印https cookies并忽略http。改进版本在这里:https://dev59.com/IGYr5IYBdhLWcg3wcJx0#36665793 - Adrian Lopez
我正在一个http网站上使用这种方法。它工作正常并返回所有http cookies。但是当我将https更改为http时,它不会打印所有的cookies,只有那些在主域上的cookies。 - Milad

19

使用这种方法可以确保获取所有的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;
            }
        }
    }
}

10

第一个答案在可移植项目中不起作用。因此这是选项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) 您可以通过domains.Keys进行迭代,并对所有键使用container.GetCookies()。但我遇到过问题,因为GetCookies期望Uri而不是所有我的键都匹配Uri模式。


-3

使用CookieContainer.GetCookies方法

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

其中url是您网站的URL。

在我的情况下,我无法像其他答案建议的那样使用反射。但是,我知道要查询的网站的URL。我认为容器不会盲目返回所有cookie,而是根据URL返回它们,因为cookie始终属于特定的URL,并且不能在与其关联的域的上下文之外使用。


我不确定为什么其他作者需要反射和特殊代码。 - Boris Zinchenko
12
OP希望得到所有cookie的列表,不考虑它们所属的url/Uri。这就是不同之处。 - granadaCoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接