2

我正在尝试让我的应用程序在外部网站上执行登录操作。我使用以下代码:

Dim enc As Encoding = Encoding.UTF8
    Dim Data As Byte() = Nothing
    Dim req As HttpWebRequest

    req = CType(Net.WebRequest.Create(URL), Net.HttpWebRequest)
    req.Method = method
    req.CookieContainer = CookieJar

    req.AllowAutoRedirect = False
    If method = "POST" Then
        req.ContentType = "application/x-www-form-urlencoded"
        Data = enc.GetBytes(PostData)
        If Data.Length > 0 Then
            req.ContentLength = Data.Length
            Dim newStream As Stream = req.GetRequestStream()
            newStream.Write(Data, 0, Data.Length)
            newStream.Flush()
            newStream.Close()
        End If
    End If

    Dim Response As Net.HttpWebResponse = CType(req.GetResponse(), Net.HttpWebResponse)

    Dim ResponseStream As IO.StreamReader = New IO.StreamReader(Response.GetResponseStream(), enc)
    Dim Html As String = ResponseStream.ReadToEnd()

    Response.Close()
    ResponseStream.Close()

    Return Html

什么有效:

  • 响应具有所有正确的“Set-Cookie”标头
  • 容器保存所有正确的 cookie(总共 5 个)

什么不起作用:

  • 容器正在正确检索所有 cookie。但并非所有 cookie 都会随下一个请求一起发送。4 个 cookie 设置正确,但最重要的一个没有发送。

未发送的 cookie 是这个:

Set-Cookie: mpSecurity="ODc2NzM2ODoxMzUODViNTg5OWM1NTNlOWMwYmMxYjUxNWZjYzJjOGQyZGU4MTc2M2M=";Version=1;Path=/;Domain=.xxxxx.nl;Discard

此 cookie 与正确发送的 cookie 之间的唯一区别在于,此 cookie 中包含“Version=1”和“Discard”...

有谁知道为什么所有检索到的 cookie 都被发送,除了上面的那个吗?

任何帮助,将不胜感激!

4

2 回答 2

2

这是 CookieContainer 中常见的已知错误:此处链接 .Net 版本低于 4.0

注意 Set-Cookie Header 的域:

Cookie # 1 -> Set-Cookie: mpSecurity="ODc2NzM2ODoxMzUODViNTg5OWM1NTNlOWMwYmMxYjUxNWZjYzJjOGQyZGU4MTc2M2M=";Version=1;Path=/;Domain=marktplaats.nl;Discard
Cookie # 2 -> Set-Cookie: mpSecurity="ODc2NzM2ODoxMzUODViNTg5OWM1NTNlOWMwYmMxYjUxNWZjYzJjOGQyZGU4MTc2M2M=";Version=1;Path=/;Domain=.marktplaats.nl;Discard

Cookie #1 在 URL 格式类似http://marktplaats.nl/...
时发送 Cookie #2 在 URL 格式类似时发送http://www.marktplaats.nl/...

因此问题


这里的解决方案#1:(更好更简单的)

    class DomainComparer : StringComparer
    {
        public override int Compare(string x, string y)
        {
            if (x == null || y == null)
            {
                return StringComparer.OrdinalIgnoreCase.Compare(x, y);
            }
            if (x.StartsWith("www.", StringComparison.OrdinalIgnoreCase))
            {
                x = x.Substring(4);
            }
            if (y.StartsWith("www.", StringComparison.OrdinalIgnoreCase))
            {
                y = y.Substring(4);
            }
            return StringComparer.OrdinalIgnoreCase.Compare(x, y);
        }

        public override bool Equals(string x, string y)
        {
            return Compare(x, y) == 0;
        }

        public override int GetHashCode(string obj)
        {
            if (obj.StartsWith("www.", StringComparison.OrdinalIgnoreCase))
            {
                obj = obj.Substring(4);
            }
            return StringComparer.OrdinalIgnoreCase.GetHashCode(obj);
        }
    }

    /// <summary>
    /// this is a hackfix for microsoft bug, where cookies are not shared between www.domain.com and domain.com
    /// </summary>
    /// <param name="cc"></param>
    static void ImproveCookieContainer(ref CookieContainer cc)
    {
        Hashtable table = (Hashtable)cc.GetType().InvokeMember(
            "m_domainTable",
            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance,
            null, cc, new object[] { });
        var comparerPreperty = table.GetType().GetField("_keycomparer", 
            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance);
        if (comparerPreperty != null)
        {
            comparerPreperty.SetValue(table, new DomainComparer());
        }
    }

解决方案#1的实现,每当您创建CookieContainer的实例时,只需调用一次该方法

void main()
{
    CookieContainer cookieJar = new CookieContainer();
    ImproveCookieContainer(ref cookieJar);
    // then use it with the WebRequest object
}

这里的解决方案#2:

  1. 不要使用 .Add(Cookie),仅使用 .Add(Uri, Cookie) 方法。
  2. 每次向容器添加 cookie 或在使用 .GetCookie 之前或系统使用容器之前调用 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];
            }
        }
    }
    

CallMeLaNN解决方案的所有功劳

于 2013-01-25T15:06:45.643 回答
1

决定为此/类似情况发布另一个解决方法,以防有人发现它有用。

我遇到了同样的问题,收到了三个 cookie,只有两个显示为从下一个 Web 请求发送。我做了一些调试,发现对我来说错误不是域本身,正如前面的答案所暗示的那样——在我的情况下,问题是返回的第三个 cookie 并没有在后续请求中重用是在 HTTPS 重定向上设置的,并且它的“安全”属性设置为真。所以我所要做的就是将下一个请求 url 从 http://.... 更改为 https://....,然后所有 cookie 都被发送过来,一切正常。

希望它可以帮助某人!

于 2014-09-29T19:42:18.873 回答