Verwenden von CookieContainer mit der WebClient-Klasse

148

Ich habe zuvor einen CookieContainer mit HttpWebRequest- und HttpWebResponse-Sitzungen verwendet, möchte ihn jetzt jedoch mit einem WebClient verwenden. Soweit ich weiß, gibt es keine integrierte Methode wie für HttpWebRequests ( request.CookieContainer). Wie kann ich Cookies von einem WebClient in einem CookieContainer sammeln?

Ich habe danach gegoogelt und folgendes Beispiel gefunden :

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

Ist das der beste Weg, es zu tun?

Maxim Zaslavsky
quelle
1
Aus meiner Sicht m_containerist nie festgelegt!? Ist es nicht immer leer?
C4d
Ich glaube, dass die HttpWebRequest-Klasse die m_container-Klasse mithilfe ihres internen Felds CookieContainer nach Bedarf ändert.
HeartWare
Das ist alles was Sie brauchen! Die Cookies aus den Antworten werden automatisch zum Container hinzugefügt.
Lionello

Antworten:

69

Ja. IMHO ist das Überschreiben von GetWebRequest () die beste Lösung für die eingeschränkte Funktionalität von WebClient. Bevor ich von dieser Option wusste, schrieb ich viel wirklich schmerzhaften Code auf der HttpWebRequest-Ebene, da WebClient fast, aber nicht ganz das tat, was ich brauchte. Die Ableitung ist viel einfacher.

Eine andere Option besteht darin, die reguläre WebClient-Klasse zu verwenden, den Cookie-Header jedoch manuell zu füllen, bevor die Anforderung gestellt wird, und dann den Set-Cookies-Header in der Antwort herauszuziehen. Es gibt Hilfsmethoden auf der Cookie Klasse , die diese Header erleichtern das Erstellen und Analysieren von : CookieContainer.SetCookies()und CookieContainer.GetCookieHeader(), respectively.

Ich bevorzuge den früheren Ansatz, da er für den Anrufer einfacher ist und weniger sich wiederholenden Code erfordert als die zweite Option. Der Ableitungsansatz funktioniert auch für mehrere Erweiterbarkeitsszenarien (z. B. Cookies, Proxys usw.) auf dieselbe Weise.

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

Aus Kommentaren

Wie formatieren Sie den Namen und den Wert des Cookies anstelle von "somecookie"?

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

Für mehrere Cookies:

wb.Headers.Add(HttpRequestHeader.Cookie, 
              "cookiename1=cookievalue1;" +
              "cookiename2=cookievalue2");
Rajeesh
quelle
Wie formatieren Sie den Namen und den Wert des Cookies anstelle von "somecookie"?
Neil N
11
@Neil N: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename = cookievalue"); Für mehrere Cookies: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename1 = cookievalue1; cookiename2 = cookievalue2");
Ian Kemp
46

Dies ist nur eine Erweiterung des Artikels, den Sie gefunden haben.


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
quelle
3
Dies hat bei @Pavel gut funktioniert, obwohl Sie diese Antwort verbessern können, indem Sie zeigen, wie die Funktionen der Klasse verwendet werden, insbesondere das Setzen und Abrufen der Cookies.
Corgalore
Danke für die Erweiterung. Zur Verwendung füge ich öffentlichen CookieContainer hinzu CookieContainer {get {return _container; } set {_container = value; }}
Igor Shubin
1
@IgorShubin Sie müssen den readonlyModifikator des containerFeldes entfernen , sonst können Sie ihn nicht in der Eigenschaft festlegen. Ich habe den Code geändert.
Hillin
1
Sollten Sie nicht überprüfen Set-CookieAntwort - Header in ReadCookies?
Achilles
2
Sie brauchen nicht wirklich die GetWebResponseund ReadCookies, da die Cookies automatisch den Behälter hinzukommen.
Lionello
15

Der HttpWebRequest ändert den ihm zugewiesenen CookieContainer. Zurückgegebene Cookies müssen nicht verarbeitet werden. Ordnen Sie einfach Ihren Cookie-Container jeder Webanforderung zu.

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
quelle
6

Ich denke, es gibt eine sauberere Möglichkeit, keinen neuen Webclient zu erstellen (und dies funktioniert auch mit Bibliotheken von Drittanbietern).

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

Jetzt müssen Sie sich nur noch für die Domains entscheiden, die Sie verwenden möchten:

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

Das bedeutet, dass JEDE Webanforderung, die an example.com geht, jetzt Ihren benutzerdefinierten Webanforderungsersteller verwendet, einschließlich des Standard-Webclients. Dieser Ansatz bedeutet, dass Sie nicht Ihren gesamten Code berühren müssen. Sie rufen das Registerpräfix einfach einmal auf und sind fertig. Sie können sich auch für das Präfix "http" registrieren, um sich überall für alles zu entscheiden.

dotMorten
quelle
Ich bin mir bei den letzten Sätzen nicht sicher. In den Dokumenten heißt es: "Die HttpWebRequest-Klasse ist standardmäßig für Dienstanforderungen für HTTP- und HTTPS-Schemata registriert. Versuche, einen anderen WebRequest-Nachkommen für diese Schemata zu registrieren, schlagen fehl."
Herohtar