158

I've previously used a CookieContainer with HttpWebRequest and HttpWebResponse sessions, but now, I want to use it with a WebClient. As far as I understand, there is no built-in method like there is for HttpWebRequests (request.CookieContainer). How can I collect cookies from a WebClient in a CookieContainer?

I googled for this and found the following sample:

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

Is this the best way to do it?

Nathan Baulch
  • 20,233
  • 5
  • 52
  • 56
Maxim Zaslavsky
  • 17,787
  • 30
  • 107
  • 173
  • 1
    From my point of view `m_container` is never set!? Isnt it always empty? – C4d Jun 13 '16 at 10:55
  • 1
    I believe that the HttpWebRequest class modifies the m_container class using its internal field CookieContainer as needed. – HeartWare Aug 04 '16 at 13:19
  • This is all you need! The cookies from the responses will get added to the container automatically. – lionello Oct 21 '16 at 04:48

5 Answers5

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

From Comments

How do you format the name and value of the cookie in place of "somecookie" ?

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

For multiple cookies:

wb.Headers.Add(HttpRequestHeader.Cookie, 
              "cookiename1=cookievalue1;" +
              "cookiename2=cookievalue2");
Sam
  • 7,252
  • 16
  • 46
  • 65
Rajeesh
  • 1,301
  • 1
  • 8
  • 2
  • How do you format the name and value of the cookie in place of "somecookie" ? – Neil N Jul 09 '12 at 15:45
  • 11
    @Neil N: wb.Headers.Add(HttpRequestHeader.Cookie, "cookiename=cookievalue"); For multiple cookies: wb.Headers.Add(HttpRequestHeader.Cookie, "cookiename1=cookievalue1; cookiename2=cookievalue2"); – Ian Kemp Aug 10 '12 at 22:53
  • This was the most easy way for me, to transport a cookie from Anglesharp to WebClient for a download-link :-) Thank you! – Steav Jul 31 '20 at 10:29
69

Yes. IMHO, overriding GetWebRequest() is the best solution to WebClient's limited functionalty. Before I knew about this option, I wrote lots of really painful code at the HttpWebRequest layer because WebClient almost, but not quite, did what I needed. Derivation is much easier.

Another option is to use the regular WebClient class, but manually populate the Cookie header before making the request and then pull out the Set-Cookies header on the response. There are helper methods on the CookieContainer class which make creating and parsing these headers easier: CookieContainer.SetCookies() and CookieContainer.GetCookieHeader(), respectively.

I prefer the former approach since it's easier for the caller and requires less repetitive code than the second option. Also, the derivation approach works the same way for multiple extensibility scenarios (e.g. cookies, proxies, etc.).

Justin Grant
  • 44,807
  • 15
  • 124
  • 208
48

This one is just extension of article you found.


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);
        }
    }
}
Pavel Savara
  • 3,427
  • 1
  • 30
  • 35
  • 3
    This worked well @Pavel, though you could improve this answer by showing how to use the features of the class, especially setting and getting the cookies on it. – Corgalore Aug 26 '14 at 15:57
  • Thx for extension. For use it I add public CookieContainer CookieContainer { get { return _container; } set { _container = value; } } – Ihor Shubin Nov 20 '14 at 14:10
  • 1
    @IgorShubin you have to remove the `readonly` modifier of the `container` field, otherwise you cannot set it in the property. I modified the code. – hillin Dec 06 '14 at 06:17
  • @hillin - agreed. However, in you edit, your default constructor will leave `CookieContainer` null, whereas in the original answer it's allocated by default. – dbc Dec 06 '14 at 07:21
  • 1
    Shouldn't you check `Set-Cookie` response header in `ReadCookies` ? – Achilles Mar 23 '15 at 22:40
  • 2
    You don't actually need the `GetWebResponse` and `ReadCookies`, since the cookies will get added to the container automatically. – lionello Oct 21 '16 at 04:49
  • @achilles httpWebResponse.Cookies == Set-Cookie Response Header. here:https://msdn.microsoft.com/en-us/library/system.web.httpresponse.cookies(v=vs.110).aspx . – bh_earth0 Jan 07 '17 at 13:22
  • Can you see this? https://stackoverflow.com/questions/48278333/php-curl-to-net-httprequest-files-uploading-to-server –  Jan 17 '18 at 07:02
16

The HttpWebRequest modifies the CookieContainer assigned to it. There is no need to process returned cookies. Simply assign your cookie container to every web request.

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;
    }
}
David Medenjak
  • 33,993
  • 14
  • 106
  • 134
Ted
  • 161
  • 1
  • 2
6

I think there's cleaner way where you don't have to create a new webclient (and it'll work with 3rd party libraries as well)

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

Now all you have to do is opt in for which domains you want to use this:

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

That means ANY webrequest that goes to example.com will now use your custom webrequest creator, including the standard webclient. This approach means you don't have to touch all you code. You just call the register prefix once and be done with it. You can also register for "http" prefix to opt in for everything everywhere.

dotMorten
  • 1,953
  • 1
  • 14
  • 10
  • I'm not sure about the last couple of sentences; the [docs](https://learn.microsoft.com/en-us/dotnet/api/system.net.webrequest.registerprefix) say: *"The HttpWebRequest class is registered to service requests for HTTP and HTTPS schemes by default. Attempts to register a different WebRequest descendant for these schemes will fail."* – Herohtar Oct 05 '19 at 23:25