Usando CookieContainer com a classe WebClient

148

Eu já usei um CookieContainer com as sessões HttpWebRequest e HttpWebResponse, mas agora quero usá-lo com um WebClient. Tanto quanto eu entendo, não existe um método interno como o HttpWebRequests ( request.CookieContainer). Como posso coletar cookies de um WebClient em um CookieContainer?

Eu pesquisei para isso e encontrei o seguinte exemplo :

public class CookieAwareWebClient : WebClient
{
    private readonly CookieContainer m_container = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);
        HttpWebRequest webRequest = request as HttpWebRequest;
        if (webRequest != null)
        {
            webRequest.CookieContainer = m_container;
        }
        return request;
    }
}

Esse é o melhor jeito de fazer isso?

Maxim Zaslavsky
fonte
1
Do meu ponto de vista m_containernunca está definido !? Não está sempre vazio?
C4d
Acredito que a classe HttpWebRequest modifique a classe m_container usando seu campo interno CookieContainer, conforme necessário.
HeartWare 4/08/16
Isso é tudo que você precisa! Os cookies das respostas serão adicionados ao contêiner automaticamente.
Lionello

Respostas:

69

Sim. IMHO, substituindo GetWebRequest () é a melhor solução para a funcionalidade limitada do WebClient. Antes de conhecer essa opção, escrevi muitos códigos realmente dolorosos na camada HttpWebRequest porque o WebClient quase, mas não exatamente, fez o que precisava. Derivação é muito mais fácil.

Outra opção é usar a classe WebClient regular, mas preencha manualmente o cabeçalho Cookie antes de fazer a solicitação e, em seguida, retire o cabeçalho Set-Cookies na resposta. Existem métodos auxiliares na classe CookieContainer que facilitam a criação e a análise desses cabeçalhos: CookieContainer.SetCookies()eCookieContainer.GetCookieHeader() , respectivamente.

Prefiro a abordagem anterior, pois é mais fácil para o chamador e requer menos código repetitivo do que a segunda opção. Além disso, a abordagem de derivação funciona da mesma maneira para vários cenários de extensibilidade (por exemplo, cookies, proxies etc.).

Justin Grant
fonte
118
 WebClient wb = new WebClient();
 wb.Headers.Add(HttpRequestHeader.Cookie, "somecookie");

De Comentários

Como você formata o nome e o valor do cookie no lugar de "somecookie"?

wb.Headers.Add(HttpRequestHeader.Cookie, "cookiename=cookievalue"); 

Para vários cookies:

wb.Headers.Add(HttpRequestHeader.Cookie, 
              "cookiename1=cookievalue1;" +
              "cookiename2=cookievalue2");
Rajeesh
fonte
Como você formata o nome e o valor do cookie no lugar de "somecookie"?
9114 Neil N
11
@ Neil N: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename = cookievalue"); Para vários cookies: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename1 = cookievalue1; cookiename2 = cookievalue2");
22812 Ian Kemp
46

Este é apenas uma extensão do artigo que você encontrou.


public class WebClientEx : WebClient
{
    public WebClientEx(CookieContainer container)
    {
        this.container = container;
    }

    public CookieContainer CookieContainer
        {
            get { return container; }
            set { container= value; }
        }

    private CookieContainer container = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest r = base.GetWebRequest(address);
        var request = r as HttpWebRequest;
        if (request != null)
        {
            request.CookieContainer = container;
        }
        return r;
    }

    protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
    {
        WebResponse response = base.GetWebResponse(request, result);
        ReadCookies(response);
        return response;
    }

    protected override WebResponse GetWebResponse(WebRequest request)
    {
        WebResponse response = base.GetWebResponse(request);
        ReadCookies(response);
        return response;
    }

    private void ReadCookies(WebResponse r)
    {
        var response = r as HttpWebResponse;
        if (response != null)
        {
            CookieCollection cookies = response.Cookies;
            container.Add(cookies);
        }
    }
}
Pavel Savara
fonte
3
Isso funcionou bem no @Pavel, embora você possa melhorar essa resposta, mostrando como usar os recursos da classe, especialmente configurando e colocando os cookies nele.
Corgalore 26/08/14
Thx para extensão. Para usá-lo, eu adiciono public CookieContainer CookieContainer {get {return _container; } defina {_container = value; }}
Igor Shubin
1
@IgorShubin você precisa remover o readonlymodificador do containercampo, caso contrário, não poderá defini-lo na propriedade Eu modifiquei o código.
hillin 6/12/2014
1
Você não deve verificar o Set-Cookiecabeçalho da resposta ReadCookies?
Achilles
2
Na verdade, você não precisa do GetWebResponsee ReadCookies, pois os cookies serão adicionados ao contêiner automaticamente.
lionello 21/10
15

O HttpWebRequest modifica o CookieContainer atribuído a ele. Não há necessidade de processar cookies retornados. Basta atribuir seu contêiner de cookies a cada solicitação da web.

public class CookieAwareWebClient : WebClient
{
    public CookieContainer CookieContainer { get; set; } = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri uri)
    {
        WebRequest request = base.GetWebRequest(uri);
        if (request is HttpWebRequest)
        {
            (request as HttpWebRequest).CookieContainer = CookieContainer;
        }
        return request;
    }
}
Ted
fonte
6

Eu acho que há uma maneira mais limpa de você não precisar criar um novo cliente da Web (e funcionará também com bibliotecas de terceiros)

internal static class MyWebRequestCreator
{
    private static IWebRequestCreate myCreator;

    public static IWebRequestCreate MyHttp
    {
        get
        {
            if (myCreator == null)
            {
                myCreator = new MyHttpRequestCreator();
            }
            return myCreator;
        }
    }

    private class MyHttpRequestCreator : IWebRequestCreate
    {
        public WebRequest Create(Uri uri)
        {
            var req = System.Net.WebRequest.CreateHttp(uri);
            req.CookieContainer = new CookieContainer();
            return req;
        }
    }
}

Agora tudo o que você precisa fazer é optar por quais domínios você deseja usar:

    WebRequest.RegisterPrefix("http://example.com/", MyWebRequestCreator.MyHttp);

Isso significa que QUALQUER solicitação de web que vá para example.com agora usará seu criador de solicitação de web personalizado, incluindo o cliente da web padrão. Essa abordagem significa que você não precisa tocar em todo o código. Você acabou de chamar o prefixo do registro uma vez e pronto. Você também pode se registrar no prefixo "http" para ativar tudo em qualquer lugar.

dotMorten
fonte
Não tenho certeza sobre as duas últimas frases; os documentos dizem: "A classe HttpWebRequest está registrada para solicitações de serviço para esquemas HTTP e HTTPS por padrão. Tentativas de registrar um descendente diferente do WebRequest para esses esquemas falharão".
Herohtar 5/10/19