如何在CookieContainer内获取cookie信息?(所有的,不针对特定域名)

20

请查看以下代码:

CookieContainer cookieJar = new CookieContainer();
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://www.google.com");
request.CookieContainer = cookieJar;

HttpWebResponse response = (HttpWebResponse)request.GetResponse();
int cookieCount = cookieJar.Count;

如何在cookieJar中获取所有的Cookie信息,而不仅仅是特定域的信息?
另外,如何向其中添加或删除Cookie?


最后,.NET 6引入了CookieContainer.GetAllCookies() - 文档链接 - aepot
7个回答

20

对我来说,这些答案都没起作用。这是我为这个问题找到的解决方案。

public static List<Cookie> List(this CookieContainer container)
{
    var cookies = new List<Cookie>();

    var table = (Hashtable)container.GetType().InvokeMember("m_domainTable",
        BindingFlags.NonPublic |
        BindingFlags.GetField |
        BindingFlags.Instance,
        null,
        container,
        null);

    foreach (string key in table.Keys)
    {
        var item = table[key];
        var items = (ICollection) item.GetType().GetProperty("Values").GetGetMethod().Invoke(item, null);
        foreach (CookieCollection cc in items)
        {
            foreach (Cookie cookie in cc)
            {
                cookies.Add(cookie);
            }
        }
    }

    return cookies;
}           

2
简短而精湛的代码。它应该被接受为答案。 - shmnff

16

使用反射可以获取CookieContainer对象中保存所有域键的私有字段,

问:我怎样才能得到那个私有字段的名称?

答:使用Reflector工具;

它声明为:

private Hashtable m_domainTable;

一旦我们获得了私有字段,就会得到域键,然后获取Cookie只需要进行简单的迭代。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Net;
using System.Collections;

namespace ConsoleApplication4
{
    static class Program
    {
        private static void Main()
        {
            CookieContainer cookies = new CookieContainer();
            cookies.Add(new Cookie("name1", "value1", "/", "domain1.com"));
            cookies.Add(new Cookie("name2", "value2", "/", "domain2.com"));

            Hashtable table = (Hashtable)cookies.GetType().InvokeMember(
                "m_domainTable",                                                      
                BindingFlags.NonPublic |                                                                           
                BindingFlags.GetField |                                                                     
                BindingFlags.Instance,                                                                      
                null,                                                                            
                cookies,
                new object[]{}
            );

            foreach (var key in table.Keys)
            {
                Uri uri = new Uri(string.Format("http://{0}/", key));

                foreach (Cookie cookie in cookies.GetCookies(uri))
                {
                    Console.WriteLine("Name = {0} ; Value = {1} ; Domain = {2}",
                        cookie.Name, cookie.Value, cookie.Domain);
                }
            }

            Console.Read();
        }
    }
}

这个方法似乎假定使用http协议,而为https创建的cookies将不会被显示。 - user1713059
@user1713059 - 它在哪里显示它只针对HTTP执行此操作?无论如何,这将获取HTTP和HTTPS的cookie。 - Parimal Raj
在控制台打印循环中有一个 string.Format("http://。使用以 http 为前缀的域名调用了 GetCookies 方法。 - user1713059
你怎么获取名称?从源代码中,还能从哪里获取呢?https://referencesource.microsoft.com/#System/net/System/Net/cookiecontainer.cs,eb89f175de026fb3 - JJS

7

感谢AppDeveloper的回答,这里是稍作修改后的扩展方法。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;

public static class CookieContainerExtension
{
    public static List<Cookie> List(this CookieContainer container)
    {
        var cookies = new List<Cookie>();

        var table = (Hashtable)container.GetType().InvokeMember("m_domainTable",
                                                                BindingFlags.NonPublic |
                                                                BindingFlags.GetField |
                                                                BindingFlags.Instance,
                                                                null,
                                                                container,
                                                                new object[] { });

        foreach (var key in table.Keys)
        {

            Uri uri = null;

            var domain = key as string;

            if (domain == null)
                continue;

            if (domain.StartsWith("."))
                domain = domain.Substring(1);

            var address = string.Format("http://{0}/", domain);

            if (Uri.TryCreate(address, UriKind.RelativeOrAbsolute, out uri) == false)
                continue;

            foreach (Cookie cookie in container.GetCookies(uri))
            {
                cookies.Add(cookie);
            }
        }

        return cookies;
    }
}

要获取列表,只需在CookieContainer上调用List()函数:
CookieContainer cookies = new CookieContainer();
cookies.Add(new Cookie("name1", "value1", "/", "www.domain1.com"));
cookies.Add(new Cookie("name2", "value2", "/", "www.domain2.com"));
List<Cookie> cookieList = cookies.List();

2
你可能想把返回语句放在foreach循环之外的方法中。 - Pixelchai

6
改进版本的PaRiMal RaJ代码。此方法将打印http和https cookies。准备好将其粘贴到您的类中。
    // Paste this dependencies in your class
    using System;
    using System.Net;
    using System.Linq;
    using System.Reflection;
    using System.Collections;
    using System.Collections.Generic;

    /// <summary>
    /// It prints all cookies in a CookieContainer. Only for testing.
    /// </summary>
    /// <param name="cookieJar">A cookie container</param>
    public void PrintCookies (CookieContainer cookieJar)
    {
        try
        {
            Hashtable table = (Hashtable) cookieJar
                .GetType().InvokeMember("m_domainTable",
                BindingFlags.NonPublic |
                BindingFlags.GetField |
                BindingFlags.Instance,
                null,
                cookieJar,
                new object[] {});


            foreach (var key in table.Keys)
            {
                // Look for http cookies.
                if (cookieJar.GetCookies(
                    new Uri(string.Format("http://{0}/", key))).Count > 0)
                {
                    Console.WriteLine(cookieJar.Count+" HTTP COOKIES FOUND:");
                    Console.WriteLine("----------------------------------");
                    foreach (Cookie cookie in cookieJar.GetCookies(
                        new Uri(string.Format("http://{0}/", key))))
                    {
                        Console.WriteLine(
                            "Name = {0} ; Value = {1} ; Domain = {2}", 
                            cookie.Name, cookie.Value,cookie.Domain);
                    }
                }

                // Look for https cookies
                if (cookieJar.GetCookies(
                    new Uri(string.Format("https://{0}/", key))).Count > 0)
                {
                    Console.WriteLine(cookieJar.Count+" HTTPS COOKIES FOUND:");
                    Console.WriteLine("----------------------------------");
                    foreach (Cookie cookie in cookieJar.GetCookies(
                        new Uri(string.Format("https://{0}/", key))))
                    {
                        Console.WriteLine(
                            "Name = {0} ; Value = {1} ; Domain = {2}", 
                            cookie.Name, cookie.Value,cookie.Domain);
                    }
                }
            }
        }
        catch(Exception e)
        {
            Console.WriteLine (e);
        }
    }

1
谢谢,你的代码可以运行,但有时我会遇到 UriFormatException 错误。你有什么建议可以让那些 cookies 打印出来吗? - Gaspare Bonventre
据我所知,没有必要为获取HTTPS和HTTP cookies设置单独的代码。如果您使用HTTPS Uri,则GetCookies()将返回两者。如果存在没有子域的域名,它还将返回重复项,例如www.example.com和example.com。如果传入www.example.com,则它将返回所有也匹配example.com的cookie。 - Chad Baldwin

4
这是一个扩展,将antfx的代码与Adrian Lopez同时使用http和https的想法结合起来。对于任何可能会发现它有用的人来说,这只是一个快速修复:
public static class CookieContainerExtensions
{
    public static List<Cookie> List(this CookieContainer container)
    {
        var cookies = new List<Cookie>();

        var table = (Hashtable)container.GetType().InvokeMember("m_domainTable",
                                                                BindingFlags.NonPublic |
                                                                BindingFlags.GetField |
                                                                BindingFlags.Instance,
                                                                null,
                                                                container,
                                                                new object[] { });

        foreach (var key in table.Keys)
        {
            var domain = key as string;

            if (domain == null)
                continue;

            if (domain.StartsWith("."))
                domain = domain.Substring(1);

            var httpAddress = string.Format("http://{0}/", domain);
            var httpsAddress = string.Format("https://{0}/", domain);

            if (Uri.TryCreate(httpAddress, UriKind.RelativeOrAbsolute, out var httpUri))
            {
                foreach (Cookie cookie in container.GetCookies(httpUri))
                {
                    cookies.Add(cookie);
                }
            }
            if (Uri.TryCreate(httpsAddress, UriKind.RelativeOrAbsolute, out var httpsUri))
            {
                foreach (Cookie cookie in container.GetCookies(httpsUri))
                {
                    cookies.Add(cookie);
                }
            }
        }

        return cookies;
    }
}

1
据我所知,这里提供的所有解决方案都忽略了cookie域路径可能与通常的“/”不同的事实。如果是这种情况,uri提取 string.Format("http://{0}/", domain); 或者 string.Format("https://{0}/", domain); 将会失败。实际上,应该是 string.Format("http://{0}/{1}/", domain, path); 问题在于如何从cookiecontainer中获取该路径值。
我采用了一种不同的方法,直接从cookiecontainer的非公共成员中提取cookie集合中的cookie。
/// <summary>
/// Extracts the cookiecollection from a cookiecontainer
/// This also works when the domain path is not the standard /
/// (Unlike most or all the solutions found in the public domain that recover the uri from the domain value
/// ignoring the path )
/// It directly extracts the cookie collections from 'non public' members of the cookiecontainer class
/// </summary>
/// <param name="container"></param>
/// <param name="collection"></param>
private void ExtractCookies (CookieContainer container, out CookieCollection collection)
{
    collection = new ();

    var table = (Hashtable)container.GetType().InvokeMember("m_domainTable",
    BindingFlags.NonPublic |
    BindingFlags.GetField |
    BindingFlags.Instance,
    null,
    container,
    null);

    foreach (string key in table.Keys)
    {
        var item = table[key];

        SortedList slcc = (SortedList)item.GetType().InvokeMember("m_list",
        BindingFlags.NonPublic |
        BindingFlags.GetField |
        BindingFlags.Instance,
        null,
        item,
        null);

        if (slcc != null)
        {
            foreach (DictionaryEntry dcc in slcc)
            {
                string path = dcc.Key.ToString(); // the domain path, not used
                CookieCollection cc = (CookieCollection)dcc.Value;
                collection.Add(cc);
            }
        }
    }
}

1
如果您要编写一个nUnit测试,它会像这样:
    [Test]
    public void Test()
    {

        CookieContainer cookies = new CookieContainer();
        cookies.Add(new Cookie("name1", "value1", "/", "www.domain1.com"));
        cookies.Add(new Cookie("name2", "value2", "/", "www.domain2.com"));

        Hashtable table = (Hashtable)cookies.GetType().InvokeMember("m_domainTable",
                                                                     BindingFlags.NonPublic |
                                                                     BindingFlags.GetField |
                                                                     BindingFlags.Instance,
                                                                     null,
                                                                     cookies,
                                                                     new object[] { });



        foreach (var key in table.Keys)
        {
            foreach (Cookie cookie in cookies.GetCookies(new Uri(string.Format("http://{0}/", key.ToString().Substring(1,key.ToString().Length - 1)))))
            {
                Assert.That(cookie != null);
                //Console.WriteLine("Name = {0} ; Value = {1} ; Domain = {2}", cookie.Name, cookie.Value,
                //                  cookie.Domain);
            }
        }



    }

请注意域和第二个foreach语句的更改。 - Rafael Fernandes

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