Używanie CookieContainer z klasą WebClient

148

Wcześniej użyłem CookieContainer z sesjami HttpWebRequest i HttpWebResponse, ale teraz chcę go używać z WebClient. O ile rozumiem, nie ma wbudowanej metody, takiej jak w przypadku HttpWebRequests ( request.CookieContainer). Jak mogę zbierać pliki cookie z WebClient w CookieContainer?

I googled to i znaleźć następujące próbki :

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

Czy to najlepszy sposób na zrobienie tego?

Maxim Zaslavsky
źródło
1
Z mojego punktu widzenia m_containernigdy nie jest ustawione !? Czy to nie zawsze jest puste?
C4d
Uważam, że klasa HttpWebRequest modyfikuje klasę m_container przy użyciu jej wewnętrznego pola CookieContainer w razie potrzeby.
HeartWare
To wszystko, czego potrzebujesz! Pliki cookie z odpowiedzi zostaną automatycznie dodane do kontenera.
lionello

Odpowiedzi:

69

Tak. IMHO, przesłanianie GetWebRequest () jest najlepszym rozwiązaniem ograniczonej funkcjonalności WebClienta. Zanim dowiedziałem się o tej opcji, napisałem wiele naprawdę bolesnego kodu w warstwie HttpWebRequest, ponieważ WebClient prawie, ale nie do końca, zrobił to, czego potrzebowałem. Wyprowadzenie jest znacznie łatwiejsze.

Inną opcją jest użycie zwykłej klasy WebClient, ale ręczne wypełnienie nagłówka Cookie przed wysłaniem żądania, a następnie wyciągnięcie nagłówka Set-Cookies z odpowiedzi. W klasie CookieContainer istnieją metody pomocnicze, które ułatwiają tworzenie i analizowanie tych nagłówków: CookieContainer.SetCookies()i CookieContainer.GetCookieHeader(), odpowiednio.

Wolę to pierwsze podejście, ponieważ jest łatwiejsze dla dzwoniącego i wymaga mniej powtarzalnego kodu niż druga opcja. Ponadto metoda wyprowadzania działa w ten sam sposób w przypadku wielu scenariuszy rozszerzalności (np. Pliki cookie, serwery proxy itp.).

Justin Grant
źródło
118
 WebClient wb = new WebClient();
 wb.Headers.Add(HttpRequestHeader.Cookie, "somecookie");

Z komentarzy

Jak sformatować nazwę i wartość pliku cookie zamiast „somecookie”?

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

W przypadku wielu plików cookie:

wb.Headers.Add(HttpRequestHeader.Cookie, 
              "cookiename1=cookievalue1;" +
              "cookiename2=cookievalue2");
Rajeesh
źródło
Jak sformatować nazwę i wartość pliku cookie zamiast „somecookie”?
Neil N,
11
@Neil N: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename = cookievalue"); W przypadku wielu plików cookie: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename1 = cookievalue1; cookiename2 = cookievalue2");
Ian Kemp
46

Ten jest tylko rozszerzeniem znalezionego artykułu.


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
źródło
3
To zadziałało dobrze @Pavel, chociaż możesz ulepszyć tę odpowiedź, pokazując, jak korzystać z funkcji klasy, zwłaszcza ustawiania i pobierania plików cookie.
Corgalore
Dzięki za przedłużenie. Aby z niego skorzystać, dodaję public CookieContainer CookieContainer {get {return _container; } set {_container = wartość; }}
Igor Shubin
1
@IgorShubin musisz usunąć readonlymodyfikator containerpola, w przeciwnym razie nie możesz go ustawić we właściwości. Zmodyfikowałem kod.
hillin
1
Nie powinieneś sprawdzać Set-Cookienagłówka odpowiedzi ReadCookies?
Achilles
2
W rzeczywistości nie potrzebujesz GetWebResponsei ReadCookies, ponieważ pliki cookie zostaną automatycznie dodane do kontenera.
lionello
15

HttpWebRequest modyfikuje przypisaną do niego CookieContainer. Nie ma potrzeby przetwarzania zwróconych plików cookie. Po prostu przypisz swój kontener cookie do każdego żądania internetowego.

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;
    }
}
Przetrząsać
źródło
6

Myślę, że istnieje bardziej przejrzysty sposób, w którym nie musisz tworzyć nowego klienta internetowego (i będzie działać również z bibliotekami innych firm)

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

Teraz wszystko, co musisz zrobić, to wybrać domeny, których chcesz używać:

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

Oznacza to, że KAŻDE zapytanie internetowe, które trafia do example.com, będzie teraz używać Twojego niestandardowego kreatora zapytań internetowych, w tym standardowego klienta internetowego. Takie podejście oznacza, że ​​nie musisz dotykać całego kodu. Wystarczy raz wywołać prefiks rejestru i skończyć z tym. Możesz także zarejestrować prefiks „http”, aby akceptować wszystko wszędzie.

dotMorten
źródło
Nie jestem pewien co do ostatnich kilku zdań; w dokumentacji jest napisane : „Klasa HttpWebRequest jest domyślnie zarejestrowana do obsługi żądań obsługi schematów HTTP i HTTPS. Próby zarejestrowania innego elementu podrzędnego WebRequest dla tych schematów zakończą się niepowodzeniem”.
Herohtar