Использование CookieContainer с классом WebClient

148

Ранее я использовал CookieContainer с сеансами HttpWebRequest и HttpWebResponse, но теперь я хочу использовать его с WebClient. Насколько я понимаю, нет встроенного метода, как для HttpWebRequests ( request.CookieContainer). Как я могу собирать куки с веб-клиента в CookieContainer?

Я погуглил и нашел следующий пример :

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

Это лучший способ сделать это?

Максим Заславский
источник
1
С моей точки зрения m_containerникогда не устанавливается !? Разве это не всегда пусто?
C4d
Я считаю, что класс HttpWebRequest изменяет класс m_container, используя его внутреннее поле CookieContainer по мере необходимости.
HeartWare
Это все что вам нужно! Файлы cookie из ответов будут добавлены в контейнер автоматически.
Лионелло

Ответы:

69

Да. ИМХО, переопределение GetWebRequest () является лучшим решением для ограниченной функциональности WebClient. Прежде чем я узнал об этой опции, я написал много действительно болезненного кода на уровне HttpWebRequest, потому что WebClient почти, но не совсем, сделал то, что мне было нужно. Вывод намного проще.

Другой вариант - использовать обычный класс WebClient, но вручную заполнить заголовок Cookie перед выполнением запроса, а затем извлечь ответный заголовок Set-Cookies. В классе CookieContainer есть вспомогательные методы, которые облегчают создание и анализ этих заголовков: CookieContainer.SetCookies()и CookieContainer.GetCookieHeader(), соответственно.

Я предпочитаю первый подход, так как он проще для вызывающего и требует меньше повторяющегося кода, чем второй вариант. Кроме того, подход деривации работает одинаково для нескольких сценариев расширяемости (например, куки, прокси и т. Д.).

Джастин Грант
источник
118
 WebClient wb = new WebClient();
 wb.Headers.Add(HttpRequestHeader.Cookie, "somecookie");

Из комментариев

Как вы форматируете имя и значение cookie вместо «somecookie»?

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

Для нескольких файлов cookie:

wb.Headers.Add(HttpRequestHeader.Cookie, 
              "cookiename1=cookievalue1;" +
              "cookiename2=cookievalue2");
Rajeesh
источник
Как вы форматируете имя и значение cookie вместо «somecookie»?
Нил Н
11
@Neil N: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename = cookievalue"); Для нескольких файлов cookie: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename1 = cookievalue1; cookiename2 = cookievalue2");
Ян Кемп
46

Это просто продолжение статьи, которую вы нашли.


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);
        }
    }
}
Павел Савара
источник
3
Это хорошо сработало @Pavel, хотя вы могли бы улучшить этот ответ, показав, как использовать функции класса, особенно настройку и получение файлов cookie для него.
Corgalore
Спасибо за расширение. Для его использования я добавляю публичный CookieContainer CookieContainer {get {return _container; } set {_container = value; }}
Игорь Шубин
1
@IgorShubin вы должны удалить readonlyмодификатор containerполя, иначе вы не сможете установить его в свойстве. Я изменил код.
Хиллин
1
Вы не должны проверить Set-Cookieзаголовок ответа в ReadCookies?
Ахиллес
2
На самом деле вам не нужны GetWebResponseи ReadCookies, так как куки будут добавлены в контейнер автоматически.
Лионелло
15

HttpWebRequest изменяет назначенный ему CookieContainer. Нет необходимости обрабатывать возвращенные куки. Просто назначьте свой контейнер cookie для каждого веб-запроса.

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;
    }
}
Тед
источник
6

Я думаю, что есть более чистый способ, при котором вам не нужно создавать новый веб-клиент (и он будет работать со сторонними библиотеками)

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

Теперь все, что вам нужно сделать, это выбрать, для каких доменов вы хотите использовать это:

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

Это означает, что ЛЮБОЙ веб-запрос, который идет на example.com, теперь будет использовать ваш собственный создатель веб-запроса, включая стандартный веб-клиент. Такой подход означает, что вам не нужно трогать весь код. Вы просто вызываете префикс регистра один раз и покончите с этим. Вы также можете зарегистрироваться для префикса "http", чтобы подписаться на все везде.

dotMorten
источник
Я не уверен насчет последней пары предложений; в документах говорится: «Класс HttpWebRequest по умолчанию регистрируется в запросах на обслуживание для схем HTTP и HTTPS. Попытки зарегистрировать другого потомка WebRequest для этих схем завершатся неудачей».
Herohtar