Como definir um cookie no HttpClient's HttpRequestMessage

246

Estou tentando usar as APIs da web HttpClientpara fazer uma postagem em um endpoint que requer login na forma de um cookie HTTP que identifica uma conta (isso é apenas algo que #ifdefsai da versão de lançamento).

Como adiciono um cookie ao HttpRequestMessage?

George Mauer
fonte

Respostas:

374

Veja como você pode definir um valor de cookie personalizado para a solicitação:

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
fonte
2
O manipulador pode ser removido da instrução use, será descartado quando o cliente http for descartado.
Kimi
17
Kimi está correto, mas você também não deve envolver seu HttpClient em um uso. Aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Robert McLaws
9
CUIDADO: se você usar apenas 1 instância do HttpClient para fazer várias solicitações, os cookies usando o CookieContainer serão armazenados em cache. É perigoso para um usuário obter o cookie de outro usuário.
Acaz Souza 28/10
31
"O HttpClient deve ser instanciado uma vez e reutilizado durante toda a vida útil de um aplicativo. Especialmente em aplicativos de servidor, a criação de uma nova instância de HttpClient para cada solicitação esgotará o número de soquetes disponíveis sob cargas pesadas ..." A partir daqui: asp .net / web-api / overview / avançado /…
SergeyT 4/17/17
4
@SergeyT, então o que fazer quando ele precisa fazer chamadas em sessão separada para o mesmo recurso? :)
AgentFire
336

A resposta aceita é a maneira correta de fazer isso na maioria dos casos. No entanto, há algumas situações em que você deseja definir o cabeçalho do cookie manualmente. Normalmente, se você definir um cabeçalho "Cookie", ele será ignorado, mas isso ocorre porque o HttpClientHandlerpadrão é usar sua CookieContainerpropriedade para cookies. Se você desabilitar isso, configurando UseCookiespara, falsepoderá definir manualmente os cabeçalhos dos cookies e eles aparecerão na solicitação, por exemplo

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
fonte
44
Venho perseguindo há vários dias um erro no qual solicitações enviadas com SendAsync não enviavam o cabeçalho do cookie; isso me ajudou a perceber que, a menos que você defina UseCookies = false no manipulador, ele não apenas usará o CookieContainer, mas também ignorará silenciosamente qualquer cookie armazenado nos cabeçalhos da solicitação! Muito obrigado!
Fernando Neira
14
Esta resposta é extremamente útil para quem tenta usar o HttpClient como proxy!
Cchamberlain 4/12/15
3
Tentando isso agora ... se funcionar ... você merece um bom e velho abraço canadense.
Maxime Rouiller
11
CUIDADO: se você usar apenas 1 instância do HttpClient para fazer várias solicitações, os cookies usando o CookieContainer serão armazenados em cache. É perigoso para um usuário obter o cookie de outro usuário.
Acaz Souza 28/10
9
Essa coisa estúpida deve gerar uma exceção quando alguém tenta adicionar um cabeçalho "Cookie" em vez de perdê-lo silenciosamente. Me custou uma hora da minha vida. Obrigado pela solução.
stmax
5

Depois de passar horas nessa questão, nenhuma das respostas acima me ajudou a encontrar uma ferramenta realmente útil.

Primeiramente, usei o Fiddler 4 da Telerik para estudar meus pedidos da Web em detalhes

Em segundo lugar, me deparei com este plugin útil para o Fiddler:

https://github.com/sunilpottumuttu/FiddlerGenerateHttpClientCode

Ele irá gerar apenas o código C # para você. Um exemplo foi:

        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;

Observe que eu tive que comentar duas linhas, pois esse plugin ainda não é totalmente perfeito, mas fez o trabalho mesmo assim.

AVISO LEGAL: Eu não sou associado ou endossado pela Telerik ou pelo autor do plugin de qualquer maneira.

Amir Sem Família
fonte
2
Esta é essencialmente a mesma resposta que esta , a única parte que tem a ver com cookies é a última adição de um cabeçalho. Observe todas as advertências nesta resposta
George Mauer
4

Para mim, a solução simples funciona para definir cookies no objeto 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
fonte
Note-se que a resposta aceite faz uma tonelada mais e lida com muito mais condições de borda do que isso. Ele pode ser usado para fazer coisas como http apenas ou cookies escopo, biscoitos de vários valores, etc etc. A resposta segundo mais alto avaliado propõe o mesmo método como este, mas com muito mais contexto e explicação
George Mauer
@GeorgeMauer pode estar certo. Ambos criando httpClient a partir de "HttpClient (manipulador)". No meu caso, estou criando _httpClient a partir de httpClientPool.GetOrCreateHttpClient ()
Waqar UlHaq
1
Mas você na verdade não mostra isso em sua resposta nem explica a diferença ou os benefícios (também não é realmente a pergunta, mas não estou preocupado com isso). Não estou tentando ser rude, é apenas importante esclarecer a quem essa resposta seria útil e que não seria melhor ajudada pelos outros.
George Mauer