Utilisation de CookieContainer avec la classe WebClient

148

J'ai déjà utilisé un CookieContainer avec des sessions HttpWebRequest et HttpWebResponse, mais maintenant, je veux l'utiliser avec un WebClient. Autant que je sache, il n'y a pas de méthode intégrée comme pour HttpWebRequests ( request.CookieContainer). Comment puis-je collecter des cookies d'un WebClient dans un CookieContainer?

J'ai cherché cela sur Google et j'ai trouvé l'exemple suivant :

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

Est-ce la meilleure façon de le faire?

Maxim Zaslavsky
la source
1
De mon point de vue m_containern'est jamais défini!? N'est-il pas toujours vide?
C4d
Je crois que la classe HttpWebRequest modifie la classe m_container en utilisant son champ interne CookieContainer selon les besoins.
HeartWare
C'est tout ce dont vous avez besoin! Les cookies des réponses seront automatiquement ajoutés au conteneur.
lionello

Réponses:

69

Oui. À mon humble avis, remplacer GetWebRequest () est la meilleure solution pour les fonctionnalités limitées de WebClient. Avant de connaître cette option, j'ai écrit beaucoup de code vraiment pénible sur la couche HttpWebRequest parce que WebClient a presque, mais pas tout à fait, fait ce dont j'avais besoin. La dérivation est beaucoup plus facile.

Une autre option consiste à utiliser la classe WebClient standard, mais à remplir manuellement l'en-tête Cookie avant de faire la demande, puis à extraire l'en-tête Set-Cookies sur la réponse. Il existe des méthodes d'assistance sur la classe CookieContainer qui facilitent la création et l'analyse de ces en-têtes: CookieContainer.SetCookies()et CookieContainer.GetCookieHeader(), respectivement.

Je préfère la première approche car elle est plus facile pour l'appelant et nécessite moins de code répétitif que la deuxième option. En outre, l'approche de dérivation fonctionne de la même manière pour plusieurs scénarios d'extensibilité (par exemple les cookies, les proxies, etc.).

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

À partir des commentaires

Comment formatez-vous le nom et la valeur du cookie à la place de "somecookie"?

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

Pour plusieurs cookies:

wb.Headers.Add(HttpRequestHeader.Cookie, 
              "cookiename1=cookievalue1;" +
              "cookiename2=cookievalue2");
Rajeesh
la source
Comment formatez-vous le nom et la valeur du cookie à la place de "somecookie"?
Neil N
11
@Neil N: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename = cookievalue"); Pour plusieurs cookies: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename1 = cookievalue1; cookiename2 = cookievalue2");
Ian Kemp
46

Celui-ci n'est qu'une extension de l'article que vous avez trouvé.


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
la source
3
Cela a bien fonctionné @Pavel, bien que vous puissiez améliorer cette réponse en montrant comment utiliser les fonctionnalités de la classe, en particulier en définissant et en obtenant les cookies.
Corgalore
Thx pour l'extension. Pour l'utiliser, j'ajoute public CookieContainer CookieContainer {get {return _container; } set {_container = valeur; }}
Igor Shubin
1
@IgorShubin vous devez supprimer le readonlymodificateur du containerchamp, sinon vous ne pouvez pas le définir dans la propriété. J'ai modifié le code.
hillin
1
Ne devriez-vous pas vérifier l'en- Set-Cookietête de la réponse ReadCookies?
Achilles
2
Vous n'avez en fait pas besoin de GetWebResponseet ReadCookies, puisque les cookies seront ajoutés automatiquement au conteneur.
lionello
15

Le HttpWebRequest modifie le CookieContainer qui lui est attribué. Il n'est pas nécessaire de traiter les cookies renvoyés. Attribuez simplement votre conteneur de cookies à chaque demande 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
la source
6

Je pense qu'il existe un moyen plus simple de ne pas avoir à créer un nouveau client Web (et cela fonctionnera également avec des bibliothèques tierces)

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

Il ne vous reste plus qu'à choisir les domaines pour lesquels vous souhaitez utiliser ceci:

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

Cela signifie que TOUTE webrequest qui va à example.com utilisera désormais votre créateur de webrequest personnalisé, y compris le client Web standard. Cette approche signifie que vous n'avez pas à toucher tout votre code. Vous appelez simplement le préfixe de registre une fois et en avez terminé. Vous pouvez également vous inscrire au préfixe "http" pour tout opter partout.

dotMorten
la source
Je ne suis pas sûr des deux dernières phrases; la documentation dit: "La classe HttpWebRequest est enregistrée pour traiter les requêtes pour les schémas HTTP et HTTPS par défaut. Les tentatives d'enregistrement d'un descendant WebRequest différent pour ces schémas échoueront."
Herohtar