Usando CookieContainer con la clase WebClient

148

Anteriormente he usado un CookieContainer con las sesiones HttpWebRequest y HttpWebResponse, pero ahora quiero usarlo con un WebClient. Por lo que yo entiendo, no hay un método incorporado como el que hay para HttpWebRequests ( request.CookieContainer). ¿Cómo puedo recolectar cookies de un WebClient en un CookieContainer?

Yo en Google para esto y encontré el siguiente ejemplo :

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;
    }
}

¿Es esta la mejor manera de hacerlo?

Maxim Zaslavsky
fuente
1
¿Desde mi punto de vista m_containernunca se establece? ¿No está siempre vacío?
C4d
Creo que la clase HttpWebRequest modifica la clase m_container usando su campo interno CookieContainer según sea necesario.
HeartWare
¡Esto es todo lo que necesitas! Las cookies de las respuestas se agregarán al contenedor automáticamente.
lionello

Respuestas:

69

Si. En mi humilde opinión, anular GetWebRequest () es la mejor solución para la funcionalidad limitada de WebClient. Antes de conocer esta opción, escribí un montón de código realmente doloroso en la capa HttpWebRequest porque WebClient casi, pero no del todo, hizo lo que necesitaba. La derivación es mucho más fácil.

Otra opción es usar la clase WebClient normal, pero llenar manualmente el encabezado de Cookie antes de realizar la solicitud y luego extraer el encabezado Set-Cookies en la respuesta. Existen métodos auxiliares en la clase CookieContainer que facilitan la creación y el análisis de estos encabezados: CookieContainer.SetCookies()y CookieContainer.GetCookieHeader(), respectivamente.

Prefiero el primer enfoque, ya que es más fácil para la persona que llama y requiere menos código repetitivo que la segunda opción. Además, el enfoque de derivación funciona de la misma manera para múltiples escenarios de extensibilidad (por ejemplo, cookies, proxies, etc.).

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

De comentarios

¿Cómo formatea el nombre y el valor de la cookie en lugar de "somecookie"?

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

Para múltiples cookies:

wb.Headers.Add(HttpRequestHeader.Cookie, 
              "cookiename1=cookievalue1;" +
              "cookiename2=cookievalue2");
Rajeesh
fuente
¿Cómo formatea el nombre y el valor de la cookie en lugar de "somecookie"?
Neil N
11
@Neil N: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename = cookievalue"); Para varias cookies: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename1 = cookievalue1; cookiename2 = cookievalue2");
Ian Kemp
46

Este es solo una extensión del artículo que encontraste.


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
fuente
3
Esto funcionó bien @Pavel, aunque podría mejorar esta respuesta mostrando cómo usar las funciones de la clase, especialmente configurando y obteniendo las cookies.
Corgalore
Thx para la extensión. Para usarlo agrego CookieContainer público CookieContainer {get {return _container; } set {_container = value; }}
Igor Shubin
1
@IgorShubin tiene que eliminar el readonlymodificador del containercampo, de lo contrario no puede configurarlo en la propiedad. Modifiqué el código.
hillin
1
¿No deberías comprobar el Set-Cookieencabezado de respuesta ReadCookies?
Aquiles
2
En realidad, no necesita el GetWebResponsey ReadCookies, ya que las cookies se agregarán al contenedor automáticamente.
lionello
15

HttpWebRequest modifica el CookieContainer asignado a él. No es necesario procesar las cookies devueltas. Simplemente asigne su contenedor de cookies a cada solicitud 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
fuente
6

Creo que hay una forma más limpia de no tener que crear un nuevo cliente web (y también funcionará con bibliotecas de terceros)

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;
        }
    }
}

Ahora todo lo que tiene que hacer es optar por qué dominios desea usar esto:

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

Eso significa que CUALQUIER webrequest que vaya a example.com ahora usará su creador de webrequest personalizado, incluido el cliente web estándar. Este enfoque significa que no tiene que tocar todo su código. Simplemente llame al prefijo de registro una vez y termine con él. También puede registrarse para el prefijo "http" para optar por todo en todas partes.

dotMorten
fuente
No estoy seguro acerca de las últimas dos oraciones; los documentos dicen: "La clase HttpWebRequest está registrada para solicitudes de servicio para esquemas HTTP y HTTPS de forma predeterminada. Los intentos de registrar un descendiente WebRequest diferente para estos esquemas fallarán".
Herohtar