Ошибка CookieContainer?

Я не понимаю, как CookieContainer обрабатывает домен, поэтому я создаю этот тест. Этот тест показывает, что cookieContainer не возвращает никаких файлов cookie для "example.com", но согласно RFC он должен возвращать как минимум 2 файла cookie.

Разве это не ошибка?

Как заставить его работать?

Вот обсуждение этой ошибки:

http://social.msdn.microsoft.com/Forums/en-US/ncl/thread/c4edc965-2dc2-4724-8f08-68815cf1dce6.

<%@ Page Language="C#" %>

<%@ Import Namespace="System.Net" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    CookieContainer getContainer()
    {
        CookieContainer result = new CookieContainer();

        Uri uri = new Uri("http://sub.example.com");
        string cookieH = @"Test1=val; domain=sub.example.com; path=/";
        result.SetCookies(uri, cookieH);

        cookieH = @"Test2=val; domain=.example.com; path=/";
        result.SetCookies(uri, cookieH);

        cookieH = @"Test3=val; domain=example.com; path=/";
        result.SetCookies(uri, cookieH);

        return result;
    }

    void Test()
    {
        CookieContainer cookie = getContainer();
        lblResult.Text += "<br>Total cookies count: " + cookie.Count + " &nbsp;&nbsp; expected: 3";

        Uri uri = new Uri("http://sub.example.com");
        CookieCollection coll = cookie.GetCookies(uri);
        lblResult.Text += "<br>For " + uri + " Cookie count: " + coll.Count + " &nbsp;&nbsp; expected: 2";

        uri = new Uri("http://other.example.com");
        coll = cookie.GetCookies(uri);
        lblResult.Text += "<br>For " + uri + " Cookie count: " + coll.Count + " &nbsp;&nbsp; expected: 2";

        uri = new Uri("http://example.com");
        coll = cookie.GetCookies(uri);
        lblResult.Text += "<br>For " + uri + " Cookie count: " + coll.Count + " &nbsp;&nbsp; expected: 2";

    }

    protected void Page_Load(object sender, EventArgs e)
    {
        Test();
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>CookieContainer Test Page</title>
</head>
<body>
    <form id="frmTest" runat="server">
    <asp:Label ID="lblResult" EnableViewState="false" runat="server"></asp:Label>
    </form>
</body>
</html>

person Salar    schedule 26.06.2009    source источник
comment
Я пробовал это много раз раньше. В итоге я сам прочитал заголовок cookie и сохранил его в другом месте.   -  person Nippysaurus    schedule 26.06.2009
comment
Мне нужно использовать CookieContainer, потому что это единственный способ отправить файлы cookie в HttpWebRequest.   -  person Salar    schedule 26.06.2009
comment
Не могу поверить, что у меня наконец-то был сценарий, когда изменение фреймворка с 4.0 на 3.5 (я не использовал 4.0) сломало мою программу. Мне потребовалось некоторое время, чтобы понять, почему файлы cookie сеанса для аутентификации внезапно пропали. Они исправили эту проблему в 4.0, поэтому изменение фреймворка внесло ошибку в мою программу :-)   -  person VVS    schedule 07.09.2010


Ответы (5)


Я только что нашел исправление этой ошибки и обсудил здесь: http://dot-net-expertise.blogspot.com/2009/10/cookiecontainer-domain-handling-bug-fix.html

Вот решение:

  1. Не используйте .Add (Cookie), используйте только метод .Add (Uri, Cookie).
  2. Вызывайте BugFix_CookieDomain каждый раз, когда вы добавляете cookie в контейнер, или перед использованием .GetCookie, или перед тем, как система использует контейнер.

    private void BugFix_CookieDomain(CookieContainer cookieContainer)
    {
        System.Type _ContainerType = typeof(CookieContainer);
        Hashtable table = (Hashtable)_ContainerType.InvokeMember("m_domainTable",
                                   System.Reflection.BindingFlags.NonPublic |
                                   System.Reflection.BindingFlags.GetField |
                                   System.Reflection.BindingFlags.Instance,
                                   null,
                                   cookieContainer,
                                   new object[] { });
        ArrayList keys = new ArrayList(table.Keys);
        foreach (string keyObj in keys)
        {
            string key = (keyObj as string);
            if (key[0] == '.')
            {
                string newKey = key.Remove(0, 1);
                table[newKey] = table[keyObj];
            }
        }
    }
    
person CallMeLaNN    schedule 08.10.2009
comment
Большое спасибо! Я потратил 2 дня на отладку, думая, что такой новичок, как я, должен научиться рисовать библиотеки самостоятельно. Но это была ошибка! - person PRINCESS FLUFF; 08.02.2010
comment
Ах ... откуда взялся _ContainerType? Мой компилятор его не найдет! - person PRINCESS FLUFF; 11.02.2010
comment
Ах! Нашел ... Вам нужно заменить _ContainerType на cookieContainer.GetType () - person PRINCESS FLUFF; 11.02.2010
comment
Просто отредактируйте его, чтобы добавить System.Type _ContainerType = typeof (CookieContainer); Обратите внимание, что используйте это для повышения производительности. Использование cookieContainer.GetType () каждый раз требует больше времени. - person CallMeLaNN; 14.03.2010
comment
Это странные вещи, за которые я ненавижу Microsoft, хотя обычно я не против них. Это может произойти при получении результатов с некоторых доменов, одним из которых является my.opera.com. - person Henry B; 26.07.2010

Я создал исправление этой проблемы, которое работает в приложениях Windows 10 / UWP / .NET Core. Проблема в том, что внутреннее устройство CookieContainer другое, но такое же дерьмовое, как и собственно .NET Framework. Так что принятое решение больше не работает.

Но вместо «исправления» CookieContainer я просто написал версию GetCookies(), которая получает все файлы cookie для определенного домена в виде строки, независимо от их «безопасного» состояния или если они имеют префикс с точкой. Не стесняйтесь изменять его по своему усмотрению, и я постараюсь реализовать его версию в будущем выпуске .NET Core.

using System.Collections.Generic;
using System.Reflection;

namespace System.Net
{

    /// <summary>
    /// Contains extensions for the <see cref="CookieContaner"/> class.
    /// </summary>
    public static class CookieContainerExtensions
    {

        /// <summary>
        /// Uses Reflection to get ALL of the <see cref="Cookie">Cookies</see> where <see cref="Cookie.Domain"/> 
        /// contains part of the specified string. Will return cookies for any subdomain, as well as dotted-prefix cookies. 
        /// </summary>
        /// <param name="cookieContainer">The <see cref="CookieContainer"/> to extract the <see cref="Cookie">Cookies</see> from.</param>
        /// <param name="domain">The string that contains part of the domain you want to extract cookies for.</param>
        /// <returns></returns>
        public static IEnumerable<Cookie> GetCookies(this CookieContainer cookieContainer, string domain)
        {
            var domainTable = GetFieldValue<dynamic>(cookieContainer, "_domainTable");
            foreach (var entry in domainTable)
            {
                string key = GetPropertyValue<string>(entry, "Key");

                if (key.Contains(domain))
                {
                    var value = GetPropertyValue<dynamic>(entry, "Value");

                    var internalList = GetFieldValue<SortedList<string, CookieCollection>>(value, "_list");
                    foreach (var li in internalList)
                    {
                        foreach (Cookie cookie in li.Value)
                        {
                            yield return cookie;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets the value of a Field for a given object instance.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> you want the value to be converted to when returned.</typeparam>
        /// <param name="instance">The Type instance to extract the Field's data from.</param>
        /// <param name="fieldName">The name of the Field to extract the data from.</param>
        /// <returns></returns>
        internal static T GetFieldValue<T>(object instance, string fieldName)
        {
            BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
            FieldInfo fi = instance.GetType().GetField(fieldName, bindFlags);
            return (T)fi.GetValue(instance);
        }

        /// <summary>
        /// Gets the value of a Property for a given object instance.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> you want the value to be converted to when returned.</typeparam>
        /// <param name="instance">The Type instance to extract the Property's data from.</param>
        /// <param name="propertyName">The name of the Property to extract the data from.</param>
        /// <returns></returns>
        internal static T GetPropertyValue<T>(object instance, string propertyName)
        {
            var pi = instance.GetType().GetProperty(propertyName);
            return (T)pi.GetValue(instance, null);
        }

    }

}
person Robert McLaws    schedule 16.09.2016
comment
Разве key.Contains(domain) на самом деле не должно быть key == domain || key[0] == '.' && domain.Contains(key.Substring(1)), поскольку в противном случае вы исключаете поддомены, что является основным моментом префикса точки? - person paulie4; 29.10.2020
comment
Contains ()! = Equals () означает, что неполная строка существует внутри проверяемой строки. Таким образом, вам не нужно подставлять подстроку для точки, а затем проверять это, потому что следующий код правильный: ".test.domain.com".Contains("domain.com") == true. Это правильно учитывает любой поддомен и любой период без необходимости манипулирования строками. - person Robert McLaws; 30.10.2020
comment
Я почти уверен, что у вас все наоборот. Значение key не включает субдомен. MDN сообщает, что если установлено Domain=mozilla.org, то файлы cookie доступны на таких поддоменах, как developer.mozilla.org, поэтому в этом примере, если кто-то пытается GetCookies() для developer.mozilla.org, key будет .mozilla.org. - person paulie4; 02.11.2020
comment
Это простое объяснение того, что происходит, почему у CookieContainer есть проблема и почему мое исправление решает проблему максимально кратко: dotnetfiddle.net/ohJVgc - person Robert McLaws; 03.11.2020
comment
Да, я знаю, что ваше исправление работает в этом конкретном случае, но я говорю, что ваше исправление не поддерживает поддомены. Вот что я говорю (ПРИМЕЧАНИЕ: в моем первом комментарии должно было быть EndsWIth(), а не Contains()): dotnetfiddle.net/fqOrXT - person paulie4; 05.11.2020

Наконец-то они это исправят: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=478521

person Salar    schedule 31.07.2009

Потерял день с этой проблемой. Ответ CallMeLaNN мне не помог (я использую .Net 4.5). В моем случае проблема заключалась в порядке установки тела запроса и настроек файлов cookie.

В этом случае файлы cookie не будут отправляться на сервер:

            var response = (HttpWebRequest)WebRequest.Create("http://localhost:4433/");

            using (var requestStream = response.GetRequestStream())
            {
               using (var streamWriter = new StreamWriter(requestStream))
               {
                    requestStream.Write(RequestContent);
               }
            }

            response.CookieContainer.Add(new Cookie("Name", "Value"));
            await response.GetResponseAsync();

Для того, чтобы все изменилось, необходим порядок:

            var response = (HttpWebRequest)WebRequest.Create("http://localhost:4433/");

            response.CookieContainer.Add(new Cookie("Name", "Value"));
            await response.GetResponseAsync();

            using (var requestStream = response.GetRequestStream())
            {
               using (var streamWriter = new StreamWriter(requestStream))
               {
                    requestStream.Write(RequestContent);
               }
            }
person Yaroslav    schedule 03.02.2016

Вот способ обойти эту ошибку: http://social.microsoft.com/Forums/en-US/netfxnetcom/thread/1297afc1-12d4-4d75-8d3f-7563222d234c Он использует отражение.

person Salar    schedule 26.06.2009