How do I set a cookie on HttpClient's HttpRequestMessage
Asked Answered
S

7

334

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?

Stanislas answered 11/9, 2012 at 16:1 Comment(0)
H
466

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();
}
Haematocele answered 11/9, 2012 at 16:57 Comment(9)
handler may be removed from using statement, it will be disposed when http client is disposed.Aerobic
Kimi is correct, but also you should not wrap your HttpClient in a using. aspnetmonsters.com/2016/08/2016-08-27-httpclientwrongNiels
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.Accost
"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: asp.net/web-api/overview/advanced/…Cat
@Cat so what does one do when he needs to make separate-session calls to the same resource? :)Kliment
@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.Massie
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.Inmost
@TomLint can you clarify: in this answer above, which has using on HttpClientHandler and HttpClient, should HttpClient(..., <disposeHandler>) be false (and code above incorrect) because of the first using will dispose the handler?Wifely
@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.Massie
C
455

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();
}
Crisscross answered 8/11, 2012 at 10:31 Comment(12)
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!Restless
This answer is extremely helpful for anyone trying to use HttpClient as a proxy!Blowgun
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.Accost
This is great! That "official" way never worked correctly in my multithreaded application. This one is perfect.Stitching
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.Slangy
This is great for testing Azure Http trigger functions where you want to pass in an HttpRequestMessage rather than actually make a call!Assegai
I have used both of the solutions but I just keep on getting Error 500.Sanborn
@AcazSouza this is a non-issue for this answer, as it disables the CookieContainer to be able to send its own cookies per request.Massie
@GregBeech How do you set a domain?Cobden
Save my life. For those who are working on Xamarin.forms project, please use this method for log out purpose!Cow
It works for me with .NET 6 without setting UseCookie=false and I am creating httpClient from IHttpClientFactory _httpCleintFactory.CreateClient() and not even using HttpClientHandlerBurkes
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.Mediation
A
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);
}
Alphitomancy answered 19/8, 2019 at 20:54 Comment(6)
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 explanationStanislas
@GeorgeMauer may be you are right. Both of them creating httpClient from "HttpClient(handler)". In my case I'm creating _httpClient from httpClientPool.GetOrCreateHttpClient()Alphitomancy
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.Stanislas
I believe this answer only works if you set UseCookies = false on the HttpClientHandler, which is the essence of @GregBeech's answer.Endanger
This works for me with C# 6.0 - no need to set UseCookies=false on the HttpClientHandlerSnuggle
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 HttpClientHandlerBurkes
T
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");
Tinworks answered 1/10, 2020 at 14:49 Comment(1)
Will using services.AddHeaderPropagation(options, add http cookies to subsequent requests?Thea
S
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.

Sanborn answered 19/9, 2018 at 13:13 Comment(1)
This is essentially the same answer as this one, the only part of it that has to do with cookies is that last addition of a header. Note all the caveats in that answerStanislas
B
1

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);
Bobbee answered 20/3, 2022 at 2:12 Comment(0)
R
-1
var cookies = new CookieContainer();
var clientHandler = new HttpClientHandler();
clientHandler.CookieContainer = cookies;
var client = new HttpClient(clientHandler, true);
Ryun answered 10/9, 2023 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.