2009-06-26 35 views
16

我很困惑CookieContainer如何处理域名,所以我创建了这个测试。 此测试显示cookieContainer不会为“example.com”返回任何cookie,但根据RFC它应返回至少2个Cookie。CookieContainer错误?

它不是一个错误?

如何让它工作?

这是一个关于此错误的讨论:

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> 
+0

我以前也尝试过这么多次。我最终自己读取cookie头并将其存储在其他地方。 – Nippysaurus 2009-06-26 06:58:59

+0

我必须使用CookieContainer,因为它是将Cookie发送到HttpWebRequest的唯一方法。 – Salar 2009-06-26 08:08:40

+0

不能相信我终于有一个场景,将框架从4.0更改为3.5(我没有使用4.0的东西)打破了我的程序。花了我一些时间弄清楚,为什么会话cookie用于身份验证突然失踪。他们在4.0中解决了这个问题,所以更改框架向我的程序中引入了一个错误:-) – VVS 2010-09-07 13:17:19

回答

24

我刚刚发现了这个bug修复和这里讨论: http://dot-net-expertise.blogspot.com/2009/10/cookiecontainer-domain-handling-bug-fix.html

这里是解决方案:

  1. 不要使用.Add(Cookie),只使用.Add(Uri,Cookie)方法。
  2. 在您使用.GetCookie或系统使用容器之前,每次向容器添加cookie或调用 时都会调用BugFix_CookieDomain。

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

失去了我一天这个问题。 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); 
       } 
      } 
0

我已经创建了针对此问题的一个可行的修复在Windows 10/UWP/.NET核心应用程序上。问题在于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); 
     } 

    } 

} 
相关问题