39

This is my recent code:

HttpClient authClient = new HttpClient();
authClient.BaseAddress = new Uri("http://localhost:4999/test_db/_session");
authClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

var user = new LoginUserSecretModel
{
    name = userKey,
    password = loginData.Password,
};
HttpResponseMessage authenticationResponse = authClient.PostAsJsonAsync("", user).Result;
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Myo Min Han
  • 1,633
  • 2
  • 12
  • 12

4 Answers4

32

The issue I have with many of the answers here is that using CookieContainer uses short-lived HttpClient objects which is not recommended.

Instead, you can simply read the "Set-Cookie" header from the response:

// httpClient is long-lived and comes from a IHttpClientFactory
HttpResponseMessage response = await httpClient.GetAsync(uri);
IEnumerable<string> cookies = response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value;

If the "Set-Cookie" header is not present, cookies will be null.

Daniel
  • 8,655
  • 5
  • 60
  • 87
  • 1
    Once we get the "cookies" string, is there a clean way to obtain a specific cookie value? It feels a bit clunky to have to parse the string to extract the actual cookie value. I assume there's probably be a helper method in .Net to handle it, but most of my researches circle back to CookieContainer which we can't use when reusing our HttpClient instance. – Chatonne Mar 11 '20 at 21:10
  • 1
    Best answer if a person wants to have both a response and the cookies from that response. In my humble opinion, parsing the string is trivial. – justian17 Apr 28 '20 at 19:28
  • 2
    KeyValuePair is not nullable – cubesnyc Jan 29 '21 at 17:43
  • 1
    Small technicality: `KeyValuePair>` cannot be tested for null so `?.Value` is a syntax error. – Hari Aug 16 '22 at 17:42
  • Thanks @cubesnyc and @Hari, I've fixed the answer and added a line describing what happens if there is no `"Set-Cookie"` header. – Daniel Aug 16 '22 at 19:52
17

Try this:

CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;

HttpClient authClient = new HttpClient(handler);

var uri = new Uri("http://localhost:4999/test_db/_session");

authClient.BaseAddress = uri;
authClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

var user = new LoginUserSecretModel
{
    name = userKey,
    password = loginData.Password,
};

HttpResponseMessage authenticationResponse = authClient.PostAsJsonAsync("", user).Result;

var responseCookies = cookies.GetCookies(uri).Cast<Cookie>();
RagtimeWilly
  • 5,265
  • 3
  • 25
  • 41
  • thank you for your answer .... but "cookie" count was 0 and nothing containing when i get HttpResponseMassage. – Myo Min Han Mar 24 '15 at 04:46
  • Are you sure cookies are being set? – RagtimeWilly Mar 24 '15 at 04:47
  • yes i already set, i found the cookies are in authenticationResponse.headers. – Myo Min Han Mar 24 '15 at 04:52
  • Are the cookies secure? See here: http://stackoverflow.com/questions/14681144/httpclient-not-storing-cookies-in-cookiecontainer – RagtimeWilly Mar 24 '15 at 05:10
  • `cookies` is a different object to `authenticationResponse`. The `cookies` object is just an empty object. `CookieContainer` is just a data structure for cookies to be put into... this answer does not put anything into it from the `authenticationResponse` object. – dsample Jan 06 '17 at 16:25
  • 3
    Also, if you are using HttpClient as a shared instance for many parallel requests (as it is recommended https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ ) , be aware that cookies in the handler might belong to a different parallel response, not the one you just received. Unless you do some locking for every request to avoid such cookie race conditions. – JustAMartin Aug 23 '19 at 12:29
6

This is what you need to get a list of cookies;

    private async Task<List<Cookie>> GetCookies(string url, string cookieName)
    {
        var cookieContainer = new CookieContainer();
        var uri = new Uri(url);
        using (var httpClientHandler = new HttpClientHandler
        {
            CookieContainer = cookieContainer
        })
        {
            using (var httpClient = new HttpClient(httpClientHandler))
            {
                await httpClient.GetAsync(uri);
                List<Cookie> cookies = cookieContainer.GetCookies(uri).Cast<Cookie>().ToList();
                return cookies;
            }
        }
    }

and if you need only one cookie value here's how

 private async Task<string> GetCookieValue(string url)
        {
            var cookieContainer = new CookieContainer();
            var uri = new Uri(url);
            using (var httpClientHandler = new HttpClientHandler
            {
                CookieContainer = cookieContainer
            })
            {
                using (var httpClient = new HttpClient(httpClientHandler))
                {
                    await httpClient.GetAsync(uri);
                    var cookie = cookieContainer.GetCookies(uri).Cast<Cookie>().FirstOrDefault(x => x.Name == cookieName);
                    return cookie?.Value;
                }
            }
        }
Alper Ebicoglu
  • 8,884
  • 1
  • 49
  • 55
4

Building on top of Daniel's answer and this answer to another question, this would be an easy way to read the cookies from an HTTP response.

// httpClient is long-lived and comes from a IHttpClientFactory
HttpResponseMessage response = await httpClient.GetAsync(uri);
CookieContainer cookies = new CookieContainer();
foreach (var cookieHeader in response.Headers.GetValues("Set-Cookie"))
    cookies.SetCookies(uri, cookieHeader);
string cookieValue = cookies.GetCookies(uri).FirstOrDefault(c => c.Name == "MyCookie")?.Value;
MarioVW
  • 2,225
  • 3
  • 22
  • 28
  • This worked better for me because I have a shared HttpClient instance and didn't need to work with cookies on all of the endpoints. – Joe Mayo Jan 28 '23 at 04:41