Використання CookieContainer з класом WebClient


148

Раніше я використовував CookieContainer з сесіями HttpWebRequest і HttpWebResponse, але тепер я хочу використовувати його з WebClient. Наскільки я розумію, немає вбудованого методу, як для HttpWebRequests (request.CookieContainer ). Як я можу збирати файли cookie з WebClient у 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

Так. IMHO, переосмислення GetWebRequest () є найкращим рішенням обмеженої функціональності WebClient. Перш ніж я дізнався про цей варіант, я написав багато дійсно болісного коду на шарі HttpWebRequest, оскільки WebClient майже, але не зовсім робив те, що мені потрібно. Виведення набагато простіше.

Інший варіант - використовувати звичайний клас WebClient, але вручну заповнити заголовок Cookie перед тим, як зробити запит, а потім витягнути заголовок Set-Cookies на відповідь. Для класу CookieContainer є допоміжні методи, які спрощують створення та аналіз цих заголовків: CookieContainer.SetCookies()і CookieContainer.GetCookieHeader(), відповідно.

Я віддаю перевагу колишньому підходу, оскільки він простіше для абонента і вимагає менш повторюваного коду, ніж другий варіант. Також підхід до деривації працює аналогічно для декількох сценаріїв розширення (наприклад, файли cookie, проксі-сервери тощо).


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");

Як ви відформатуєте ім'я та значення файлу cookie замість "somecookie"?
— Ніл N

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

Thx для розширення. Для його використання я додаю публічний CookieContainer CookieContainer {get {return _container; } встановити {_container = значення; }}
— Ігор Шубін

1
@IgorShubin вам потрібно буде видалити readonlyмодифікатор containerполя, інакше ви не можете встановити його у властивості. Я змінив код.
— hillin

1
Чи не слід перевірити Set-Cookieзаголовок відповіді ReadCookies?
— Ахілл

2
Насправді вам GetWebResponseі не потрібно ReadCookies, оскільки файли cookie будуть додані до контейнера автоматично.
— Ліонелло

15

HttpWebRequest змінює призначений йому CookieContainer. Немає необхідності обробляти повернені файли cookie. Просто призначіть контейнер 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, тепер використовуватиме ваш користувальницький веб-запит, включаючи стандартний webclient. Такий підхід означає, що вам не доведеться торкатися всього коду. Ви просто зателефонуєте до префіксу реєстру один раз і зробите це з ним. Ви також можете зареєструвати префікс "http", щоб увімкнути все, що скрізь.


Я не впевнений у останніх кількох реченнях; що документи говорять: «Клас HttpWebRequest зареєстрований для обслуговування запитів по HTTP і HTTPS схеми за замовчуванням Спроби зареєструвати інший WebRequest нащадок цих схеми зазнають невдачі.» .
— Герохтар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.