Comment définir un cookie sur HttpRequestMessage de HttpClient

247

J'essaie d'utiliser les API Web HttpClientpour publier un message sur un point de terminaison qui nécessite une connexion sous la forme d'un cookie HTTP qui identifie un compte (ce n'est que quelque chose qui est #ifdefsorti de la version finale).

Comment ajouter un cookie au HttpRequestMessage?

George Mauer
la source

Réponses:

374

Voici comment vous pouvez définir une valeur de cookie personnalisée pour la demande:

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();
}
Darin Dimitrov
la source
2
peut être supprimé de l'instruction using, il sera supprimé lorsque le client http sera supprimé.
Kimi
17
Kimi a raison, mais vous ne devez pas non plus envelopper votre HttpClient dans une utilisation. aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Robert McLaws
9
ATTENTION: si vous utilisez une seule instance de HttpClient pour effectuer plusieurs requêtes, les cookies utilisant CookieContainer seront mis en cache. Est dangereux pour un utilisateur d'obtenir le cookie d'un autre utilisateur.
Acaz Souza
31
"HttpClient est destiné à être instancié une fois et réutilisé tout au long de la vie d'une application. Surtout dans les applications serveur, la création d'une nouvelle instance HttpClient pour chaque demande épuisera le nombre de sockets disponibles sous de lourdes charges ..." D'ici: asp .net / web-api / aperçu / avancé /…
SergeyT
4
@SergeyT, alors que faire quand il a besoin de faire des appels de session séparée vers la même ressource? :)
AgentFire
337

La réponse acceptée est la bonne façon de procéder dans la plupart des cas. Cependant, dans certaines situations, vous souhaitez définir manuellement l'en-tête du cookie. Normalement, si vous définissez un en-tête "Cookie", il est ignoré, mais c'est parce que par HttpClientHandlerdéfaut utilise sa CookieContainerpropriété pour les cookies. Si vous désactivez cela, en définissant UseCookiessur falsevous pouvez définir manuellement les en-têtes de cookies et ils apparaîtront dans la demande, par exemple

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
la source
44
Je poursuis depuis plusieurs jours une erreur dans laquelle les demandes envoyées avec SendAsync n'ont pas envoyé l'en-tête du cookie; cela m'a aidé à réaliser que, sauf si vous définissez UseCookies = false dans le gestionnaire, il utilisera non seulement le CookieContainer, mais ignorera également silencieusement tout cookie stocké dans les en-têtes de demande! Merci beaucoup!
Fernando Neira
14
Cette réponse est extrêmement utile pour quiconque essaie d'utiliser HttpClient comme proxy!
cchamberlain
3
Essayer ceci maintenant ... si ça marche ... vous méritez un bon câlin canadien.
Maxime Rouiller
11
ATTENTION: si vous utilisez une seule instance de HttpClient pour effectuer plusieurs requêtes, les cookies utilisant CookieContainer seront mis en cache. Est dangereux pour un utilisateur d'obtenir le cookie d'un autre utilisateur.
Acaz Souza
9
Cette chose stupide devrait lever une exception lorsque quelqu'un essaie d'ajouter un en-tête "Cookie" au lieu de le perdre silencieusement. Ça m'a coûté une heure de ma vie. Merci pour la solution.
stmax
5

Après avoir passé des heures sur ce problème, aucune des réponses ci-dessus ne m'a aidé, j'ai donc trouvé un outil vraiment utile.

Tout d'abord, j'ai utilisé Fiddler 4 de Telerik pour étudier mes demandes Web en détail

Deuxièmement, je suis tombé sur ce plugin utile pour Fiddler:

https://github.com/sunilpottumuttu/FiddlerGenerateHttpClientCode

Il va juste générer le code C # pour vous. Un exemple était:

        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;

Notez que j'ai dû commenter deux lignes car ce plugin n'est pas encore totalement parfait mais il a quand même fait le travail.

AVERTISSEMENT: je ne suis pas associé ou approuvé par Telerik ou l'auteur du plugin de toute façon.

Amir sans famille
la source
2
C'est essentiellement la même réponse que celle-ci , la seule partie qui a à voir avec les cookies est la dernière addition d'un en-tête. Notez toutes les mises en garde dans cette réponse
George Mauer
4

Pour moi, la solution simple fonctionne pour définir des cookies dans l' objet HttpRequestMessage .

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
la source
Notez que la réponse acceptée fait une tonne de plus et gère beaucoup plus de conditions de bord que cela. Il peut être utilisé pour faire des choses comme http uniquement ou des cookies de portée, des cookies à valeurs multiples, etc. La deuxième réponse la mieux notée propose la même méthode que celle-ci mais avec beaucoup plus de contexte et d'explications
George Mauer
@GeorgeMauer a peut-être raison. Tous deux créant httpClient à partir de "HttpClient (gestionnaire)". Dans mon cas, je crée _httpClient à partir de httpClientPool.GetOrCreateHttpClient ()
Waqar UlHaq
1
Mais vous ne montrez pas réellement cela dans votre réponse et n'expliquez pas la différence ou les avantages (ce n'est pas non plus la question, mais cela ne m'inquiète pas). Je n'essaie pas d'être impoli, il est juste important d'être clair à qui cette réponse serait utile et qui ne serait pas mieux aidée par les autres.
George Mauer