将CookieContainer与WebClient类一起使用


148

我以前在CookieHttpContainer和HttpWebRequest和HttpWebResponse会话中使用过,但是现在,我想在WebClient中使用它。据我了解,没有像HttpWebRequests(request.CookieContainer)那样的内置方法。如何在CookieContainer中从WebClient收集cookie?

我对此进行了搜索,发现了以下示例

public class CookieAwareWebClient : WebClient
{
    private readonly CookieContainer m_container = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);
        HttpWebRequest webRequest = request as HttpWebRequest;
        if (webRequest != null)
        {
            webRequest.CookieContainer = m_container;
        }
        return request;
    }
}

这是最好的方法吗?


1
从我的角度来看m_container,永远都不会设定!它是否总是空的?
C4d

我相信HttpWebRequest类可以根据需要使用其内部字段CookieContainer修改m_container类。
HeartWare '16

这就是您所需要的!来自响应的cookie将自动添加到容器中。
lionello '16

Answers:


69

是。恕我直言,覆盖GetWebRequest()是WebClient有限功能的最佳解决方案。在我知道此选项之前,我在HttpWebRequest层上编写了许多非常痛苦的代码,因为WebClient几乎(但不是完全)完成了我需要的工作。推导要容易得多。

另一种选择是使用常规WebClient类,但在发出请求之前手动填充Cookie头,然后在响应上拉出Set-Cookies头。CookieContainer类上有一些辅助方法,这些方法使创建和解析这些标头更加容易:CookieContainer.SetCookies()CookieContainer.GetCookieHeader()

我喜欢前一种方法,因为它比第二种方法更容易调用,并且所需的重复代码更少。同样,派生方法对于多种可扩展性方案(例如cookie,代理等)也以相同的方式工作。


118
 WebClient wb = new WebClient();
 wb.Headers.Add(HttpRequestHeader.Cookie, "somecookie");

来自评论

您如何格式化cookie的名称和值来代替“ somecookie”?

wb.Headers.Add(HttpRequestHeader.Cookie, "cookiename=cookievalue"); 

对于多个Cookie:

wb.Headers.Add(HttpRequestHeader.Cookie, 
              "cookiename1=cookievalue1;" +
              "cookiename2=cookievalue2");

您如何格式化cookie的名称和值来代替“ somecookie”?
尼尔N

11
@Neil N:wb.Headers.Add(HttpRequestHeader.Cookie,“ cookiename = cookievalue”); 对于多个Cookie:wb.Headers.Add(HttpRequestHeader.Cookie,“ cookiename1 = cookievalue1; cookiename2 = cookievalue2”);
伊恩·坎普

46

这只是您找到的文章的扩展。


public class WebClientEx : WebClient
{
    public WebClientEx(CookieContainer container)
    {
        this.container = container;
    }

    public CookieContainer CookieContainer
        {
            get { return container; }
            set { container= value; }
        }

    private CookieContainer container = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest r = base.GetWebRequest(address);
        var request = r as HttpWebRequest;
        if (request != null)
        {
            request.CookieContainer = container;
        }
        return r;
    }

    protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
    {
        WebResponse response = base.GetWebResponse(request, result);
        ReadCookies(response);
        return response;
    }

    protected override WebResponse GetWebResponse(WebRequest request)
    {
        WebResponse response = base.GetWebResponse(request);
        ReadCookies(response);
        return response;
    }

    private void ReadCookies(WebResponse r)
    {
        var response = r as HttpWebResponse;
        if (response != null)
        {
            CookieCollection cookies = response.Cookies;
            container.Add(cookies);
        }
    }
}

3
@Pavel效果很好,尽管您可以通过显示如何使用类的功能(尤其是设置和获取Cookie)来改善此答案。
Corgalore 2014年

Thx扩展。为了使用它,我添加了公共CookieContainer CookieContainer {get {return _container; }设置{_container =值;}
伊戈尔·舒宾

1
@IgorShubin必须删除字段的readonly修饰符container,否则无法在属性中进行设置。我修改了代码。
Hillin 2014年

1
您不应该在中检查Set-Cookie响应头ReadCookies吗?
阿喀琉斯(Achilles)

2
实际上,您并不需要GetWebResponseReadCookies,因为cookie会自动添加到容器中。
lionello

15

HttpWebRequest修改分配给它的CookieContainer。无需处理返回的cookie。只需将Cookie容器分配给每个Web请求即可。

public class CookieAwareWebClient : WebClient
{
    public CookieContainer CookieContainer { get; set; } = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri uri)
    {
        WebRequest request = base.GetWebRequest(uri);
        if (request is HttpWebRequest)
        {
            (request as HttpWebRequest).CookieContainer = CookieContainer;
        }
        return request;
    }
}

6

我认为,您无需创建新的Web客户端即可采用一种更简洁的方式(它也可以与第三方库一起使用)

internal static class MyWebRequestCreator
{
    private static IWebRequestCreate myCreator;

    public static IWebRequestCreate MyHttp
    {
        get
        {
            if (myCreator == null)
            {
                myCreator = new MyHttpRequestCreator();
            }
            return myCreator;
        }
    }

    private class MyHttpRequestCreator : IWebRequestCreate
    {
        public WebRequest Create(Uri uri)
        {
            var req = System.Net.WebRequest.CreateHttp(uri);
            req.CookieContainer = new CookieContainer();
            return req;
        }
    }
}

现在,您要做的就是选择要使用哪个域:

    WebRequest.RegisterPrefix("http://example.com/", MyWebRequestCreator.MyHttp);

这意味着任何转到example.com的webrequest现在都将使用您的自定义webrequest创建者,包括标准的webclient。这种方法意味着您不必触摸所有代码。您只需调用一次寄存器前缀即可完成操作。您还可以注册“ http”前缀,以选择加入任何地方。


我不确定最后两句话;该文件说:“HttpWebRequest类被注册到服务请求的默认HTTP和HTTPS模式尝试注册这些计划将会失败不同的WebRequest后代。”
Herohtar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.