Question

Je ne comprends pas comment CookieContainer gère le domaine. Je crée donc ce test. Ce test montre que cookieContainer ne renvoie aucun cookie pour & "Exemple.com &"; mais selon RFC, il devrait renvoyer au moins 2 cookies.

N’est-ce pas un bug?

Comment le faire fonctionner?

Voici une discussion sur ce bogue:

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>
Était-ce utile?

La solution

Je viens de trouver le correctif pour ce bogue et d'en discuter ici: http: // point-net-expertise .blogspot.com / 2009/10 / cookiecontainer-domain-handling-bug-fix.html

Voici la solution:

  1. N'utilisez pas la méthode .Add (Cookie), utilisez uniquement la méthode .Add (Uri, Cookie).
  2. Appelez BugFix_CookieDomain chaque fois que vous ajoutez un cookie au conteneur ou avant d'utiliser .GetCookie ou avant que le système utilise le conteneur.

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

Autres conseils

//bug fix, exists only in 3.5 FW, please wrap it with defines
//http://dot-net-expertise.blogspot.com/2009/10/cookiecontainer-domain-handling-bug-fix.html
if(!value.Contains("://www.")) //we are going to hit the bug
{
    string urlWWW = value.Replace("://", "://www.");
    Uri uriWWW = new Uri(urlWWW);
    foreach (Cookie c in _cookieContainer.GetCookies(uriWWW))
        if (c.Domain.StartsWith("."))
            request.Headers["Cookies"] += c.Name + "=" + c.Value + ";"; //manually add the cookies
}
//~bug fix

J'ai perdu ma journée avec ce problème. La réponse de CallMeLaNN ne m'a pas aidé (j'utilise .Net 4.5). Dans mon cas, le problème était lié à l'ordre de configuration du corps de la demande et des paramètres de cookies.

Dans ce cas, les cookies ne seront pas envoyés au serveur:

            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();

Pour que le changement fonctionne, l'ordre est obligatoire:

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

J'ai créé un correctif pour ce problème qui fonctionne sur les applications Windows 10 / UWP / .NET Core. Le problème est que les éléments internes de CookieContainer sont différents, mais ils sont tout aussi fous que dans le .NET Framework proprement dit. La solution acceptée ne fonctionne donc plus.

Mais au lieu de " réparer " le GetCookies(), je viens d'écrire une version de <=> qui récupère tous les cookies d'un domaine particulier avec une chaîne, quel que soit leur " secure " état ou si elles sont préfixées par un point. N'hésitez pas à le modifier à votre guise, et nous verrons comment en implémenter une version dans une future version .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);
        }

    }

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top