328

I am trying to use the web api's HttpClient to do a post to an endpoint that requires login in the form of an HTTP cookie that identifies an account (this is only something that is #ifdef'ed out of the release version).

How do I add a cookie to the HttpRequestMessage?

yetAnotherSE
  • 3,178
  • 6
  • 26
  • 33
George Mauer
  • 117,483
  • 131
  • 382
  • 612

6 Answers6

460

Here's how you could set a custom cookie value for the request:

var baseAddress = new Uri("http://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("foo", "bar"),
        new KeyValuePair<string, string>("baz", "bazinga"),
    });
    cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value"));
    var result = await client.PostAsync("/test", content);
    result.EnsureSuccessStatusCode();
}
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 2
    handler may be removed from using statement, it will be disposed when http client is disposed. – Kimi Aug 01 '16 at 16:21
  • 27
    Kimi is correct, but also you should not wrap your HttpClient in a using. http://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ – Robert McLaws Sep 14 '16 at 18:42
  • 17
    CAUTION: if you use just 1 instance of HttpClient to do several requests, cookies using CookieContainer is going cached. Is dangerous to a user get the cookie from another user. – Acaz Souza Oct 28 '16 at 13:13
  • 48
    "HttpClient is intended to be instantiated once and re-used throughout the life of an application. Especially in server applications, creating a new HttpClient instance for every request will exhaust the number of sockets available under heavy loads..." From here: https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client – SergeyT Jan 04 '17 at 00:20
  • 8
    @SergeyT so what does one do when he needs to make separate-session calls to the same resource? :) – AgentFire Jul 12 '18 at 19:01
  • 6
    @RobertMcLaws not in the use-case as shown here. However, in discussions on the .NET GitHub it was brought to my attention that the problem does not lie in the HttpClient being disposed, but rather the HttpClientHandler. If you keep a static instance of the HttpClientHandler and pass that to the HttpClient constructor with `disposeHandler` set to false, you can create and dispose however many HttpClient instances you like, as it's actually the HttpMessageHandler that owns the resources, and not HttpClient. – Tom Lint Jan 09 '19 at 12:23
  • 2
    If the cookie is user based and change from request to request use Greg Beech's answer bellow. In this way you can reuse the same httpclient without race conditions for the cookie value. – alcoforado Nov 01 '19 at 15:01
  • @TomLint can you clarify: in this answer above, which has `using` on `HttpClientHandler` and `HttpClient`, should `HttpClient(..., )` be `false` (and code above incorrect) because of the first `using` will dispose the handler? – jws Jan 10 '22 at 21:50
  • @jws, yes. The `disposeHandler` parameter should be `false` and the `HttpClientHandler` should _not_ be in a using block, but instead stored at the highest appropriate level for your use case, such as the current class or even at application level, if you only ever use a single HttpMessageHandler in your application. Though in that case, you might as well just have a single, static HttpClient instance that you reuse. – Tom Lint Jan 18 '22 at 10:05
439

The accepted answer is the correct way to do this in most cases. However, there are some situations where you want to set the cookie header manually. Normally if you set a "Cookie" header it is ignored, but that's because HttpClientHandler defaults to using its CookieContainer property for cookies. If you disable that then by setting UseCookies to false you can set cookie headers manually and they will appear in the request, e.g.

var baseAddress = new Uri("http://example.com");
using (var handler = new HttpClientHandler { UseCookies = false })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
    var message = new HttpRequestMessage(HttpMethod.Get, "/test");
    message.Headers.Add("Cookie", "cookie1=value1; cookie2=value2");
    var result = await client.SendAsync(message);
    result.EnsureSuccessStatusCode();
}
Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • 68
    I've been chasing for several days an error in which requests sent with SendAsync did not send the cookie header; this helped me realize that, unless you set UseCookies = false in the Handler, it will not only use the CookieContainer, but also _silently ignore_ any Cookie stored in the request headers! Thank you so much! – Fernando Neira Apr 06 '13 at 18:23
  • 18
    This answer is extremely helpful for anyone trying to use HttpClient as a proxy! – cchamberlain Dec 04 '15 at 18:49
  • 14
    CAUTION: if you use just 1 instance of HttpClient to do several requests, cookies using CookieContainer is going cached. Is dangerous to a user get the cookie from another user. – Acaz Souza Oct 28 '16 at 13:13
  • This is great! That "official" way never worked correctly in my multithreaded application. This one is perfect. – Al Kepp Jan 06 '18 at 20:54
  • 21
    That stupid thing should throw an exception when someone tries to add a "Cookie" header instead of silently loosing it. Cost me an hour of my life. Thanks for the solution. – stmax Jan 20 '18 at 19:40
  • This is great for testing Azure Http trigger functions where you want to pass in an HttpRequestMessage rather than actually make a call! – Nich Overend Jan 23 '18 at 13:40
  • 1
    I have used both of the solutions but I just keep on getting Error 500. – Amir Hajiha Jun 19 '18 at 10:58
  • 3
    @AcazSouza this is a non-issue for this answer, as it disables the CookieContainer to be able to send its own cookies per request. – Tom Lint Jan 09 '19 at 11:30
  • 1
    @GregBeech How do you set a domain? – Reft Nov 14 '19 at 08:16
  • Save my life. For those who are working on Xamarin.forms project, please use this method for log out purpose! – Vanderwood Feb 05 '22 at 06:49
  • It works for me with .NET 6 without setting `UseCookie=false` and I am creating httpClient from `IHttpClientFactory` `_httpCleintFactory.CreateClient()` and not even using `HttpClientHandler` – Dark_Knight Jul 05 '22 at 07:29
  • I did some testing today, and I noticed that while `UseCookies = false` is necessary in a .NET Framework 4.8 application, it was not necessary in .NET Core 3.1. I did not try older versions of .NET Core. – Rudey Jul 05 '23 at 16:48
10

For me the simple solution works to set cookies in HttpRequestMessage object.

protected async Task<HttpResponseMessage> SendRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken = default(CancellationToken))
{
    requestMessage.Headers.Add("Cookie", $"<Cookie Name 1>=<Cookie Value 1>;<Cookie Name 2>=<Cookie Value 2>");

    return await _httpClient.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);
}
Waqar UlHaq
  • 6,144
  • 2
  • 34
  • 42
  • Note that the accepted answer does a *ton* more and handles a lot more edge conditions than this. It can be used to do things like http only or scoped cookies, multivalue cookies, etc etc. The second highest rated answer proposes the same method as this but with a lot more context and explanation – George Mauer Aug 19 '19 at 21:01
  • @GeorgeMauer may be you are right. Both of them creating httpClient from "HttpClient(handler)". In my case I'm creating _httpClient from httpClientPool.GetOrCreateHttpClient() – Waqar UlHaq Aug 20 '19 at 12:03
  • 3
    But you don't actually show that in your answer nor explain the difference or the benefits (it also is not actually the question, but I'm not worried about that). I'm not trying to be rude, its just important to be clear who this answer would be helpful to that would not be better helped by the others. – George Mauer Aug 20 '19 at 18:46
  • 2
    I believe this answer only works if you set `UseCookies = false` on the `HttpClientHandler`, which is the essence of @GregBeech's [answer](https://stackoverflow.com/a/13287224/). – Johann May 17 '21 at 23:28
  • 3
    This works for me with C# 6.0 - no need to set `UseCookies=false` on the `HttpClientHandler` – Ben Apr 20 '22 at 12:23
  • This also works for me with .NET 6 without setting `UseCookie=false` and I am creating httpClient from `IHttpClientFactory` `_httpCleintFactory.CreateClient()` and not even using `HttpClientHandler` – Dark_Knight Jul 05 '22 at 07:28
9

I had a similar problem and for my AspNetCore 3.1 application the other answers to this question were not working. I found that configuring a named HttpClient in my Startup.cs and using header propagation of the Cookie header worked perfectly. It also avoids all the concerns about proper disposition of your handler and client. Note if propagation of the request cookies is not what you need (sorry Op) you can set your own cookies when configuring the client factory.

Configure Services with IServiceCollection

services.AddHttpClient("MyNamedClient").AddHeaderPropagation();
services.AddHeaderPropagation(options =>
{
    options.Headers.Add("Cookie");
});

Configure with IApplicationBuilder

builder.UseHeaderPropagation();
  • Inject the IHttpClientFactory into your controller or middleware.
  • Create your client using var client = clientFactory.CreateClient("MyNamedClient");
KevM
  • 2,496
  • 1
  • 22
  • 25
8

After spending hours on this issue, none of the answers above helped me so I found a really useful tool.

Firstly, I used Telerik's Fiddler 4 to study my Web Requests in details

Secondly, I came across this useful plugin for Fiddler:

https://github.com/sunilpottumuttu/FiddlerGenerateHttpClientCode

It will just generate the C# code for you. An example was:

        var uriBuilder = new UriBuilder("test.php", "test");
        var httpClient = new HttpClient();


        var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, uriBuilder.ToString());



        httpRequestMessage.Headers.Add("Host", "test.com");
        httpRequestMessage.Headers.Add("Connection", "keep-alive");
     //   httpRequestMessage.Headers.Add("Content-Length", "138");
        httpRequestMessage.Headers.Add("Pragma", "no-cache");
        httpRequestMessage.Headers.Add("Cache-Control", "no-cache");
        httpRequestMessage.Headers.Add("Origin", "test.com");
        httpRequestMessage.Headers.Add("Upgrade-Insecure-Requests", "1");
    //    httpRequestMessage.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
        httpRequestMessage.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");
        httpRequestMessage.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
        httpRequestMessage.Headers.Add("Referer", "http://www.translationdirectory.com/");
        httpRequestMessage.Headers.Add("Accept-Encoding", "gzip, deflate");
        httpRequestMessage.Headers.Add("Accept-Language", "en-GB,en-US;q=0.9,en;q=0.8");
        httpRequestMessage.Headers.Add("Cookie", "__utmc=266643403; __utmz=266643403.1537352460.3.3.utmccn=(referral)|utmcsr=google.co.uk|utmcct=/|utmcmd=referral; __utma=266643403.817561753.1532012719.1537357162.1537361568.5; __utmb=266643403; __atuvc=0%7C34%2C0%7C35%2C0%7C36%2C0%7C37%2C48%7C38; __atuvs=5ba2469fbb02458f002");


        var httpResponseMessage = httpClient.SendAsync(httpRequestMessage).Result;

        var httpContent = httpResponseMessage.Content;
        string result = httpResponseMessage.Content.ReadAsStringAsync().Result;

Note that I had to comment out two lines as this plugin is not totally perfect yet but it did the job nevertheless.

DISCLAIMER: I am not associated or endorsed by either Telerik or the plugin's author in anyway.

Amir Hajiha
  • 836
  • 8
  • 20
  • 6
    This is essentially the same answer [as this one](https://stackoverflow.com/a/13287224/5056), the only part of it that has to do with cookies is that last addition of a header. Note all the caveats in that answer – George Mauer Sep 19 '18 at 22:56
0

If you want to use HttpClient to send a request that require the user to be logged in, this means you need to do the login process then receive the cookies and send these cookies to the request that require login.

I did this in testing an Action called IsLoggedIn. This action checks if the user is logged using the cookies in the HttpRequest.

What I did in testing this action is:

string Login = JsonConvert.SerializeObject(new LoginViewModel()
{
    Email = userFromDb.Email,
    Password = "****",
    RememberMe = false
}); ;
StringContent LoginhttpContent = new(Login, Encoding.UTF8, "application/json");
var Login_response = await _httpClient.PostAsync(
    HelperFunctions.getUrl(HelperFunctions.AcctounController.name, HelperFunctions.AcctounController.Login),
    LoginhttpContent);
Assert.Equal(HttpStatusCode.OK, Login_response.StatusCode);

//receive cookies from the login response
var cookies = Login_response.Headers.GetValues(HeaderNames.SetCookie);
//Add the cookies to the DefaultRequestHeaders of the _httpClient
_httpClient.DefaultRequestHeaders.Add("Cookie",cookies);
var IsLoggedIn_response = await _httpClient.GetAsync(HelperFunctions.getUrl(
    HelperFunctions.AcctounController.name,
    HelperFunctions.AcctounController.IsLoggedIn));
Assert.Equal("true",IsLoggedIn_response.Content.ReadAsStringAsync().Result);
Hille
  • 2,123
  • 22
  • 39