Menggunakan CookieContainer dengan kelas WebClient

148

Saya sebelumnya menggunakan CookieContainer dengan sesi HttpWebRequest dan HttpWebResponse, tapi sekarang, saya ingin menggunakannya dengan WebClient. Sejauh yang saya mengerti, tidak ada metode built-in seperti ada untuk HttpWebRequests ( request.CookieContainer). Bagaimana saya bisa mengumpulkan cookie dari WebClient di CookieContainer?

Saya mencari di Google untuk ini dan menemukan sampel berikut :

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

Apakah ini cara terbaik untuk melakukannya?

Maxim Zaslavsky
sumber
1
Dari sudut pandang saya m_containertidak pernah diatur !? Bukankah selalu kosong?
C4d
Saya percaya bahwa kelas HttpWebRequest memodifikasi kelas m_container menggunakan bidang internal CookieContainer sesuai kebutuhan.
HeartWare
Ini yang kamu butuhkan! Cookie dari tanggapan akan ditambahkan ke wadah secara otomatis.
lionello

Jawaban:

69

Iya. IMHO, mengesampingkan GetWebRequest () adalah solusi terbaik untuk fungsionalitas terbatas WebClient. Sebelum saya tahu tentang opsi ini, saya menulis banyak kode yang sangat menyakitkan di lapisan HttpWebRequest karena WebClient hampir, tetapi tidak cukup, melakukan apa yang saya butuhkan. Penurunan jauh lebih mudah.

Opsi lain adalah dengan menggunakan kelas WebClient biasa, tetapi secara manual mengisi header Cookie sebelum membuat permintaan dan kemudian menarik header Set-Cookies pada respon. Ada metode pembantu pada kelas CookieContainer yang membuat membuat dan mengurai header ini lebih mudah: CookieContainer.SetCookies()dan CookieContainer.GetCookieHeader(), masing-masing.

Saya lebih suka pendekatan yang pertama karena lebih mudah untuk penelepon dan membutuhkan kode berulang lebih sedikit daripada opsi kedua. Juga, pendekatan derivasi bekerja dengan cara yang sama untuk beberapa skenario perluasan (misalnya cookie, proksi, dll.).

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

Dari Komentar

Bagaimana Anda memformat nama dan nilai cookie sebagai ganti "somecookie"?

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

Untuk banyak cookie:

wb.Headers.Add(HttpRequestHeader.Cookie, 
              "cookiename1=cookievalue1;" +
              "cookiename2=cookievalue2");
Rajeesh
sumber
Bagaimana Anda memformat nama dan nilai cookie sebagai ganti "somecookie"?
Neil N
11
@Neil N: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename = cookievalue"); Untuk banyak cookie: wb.Headers.Add (HttpRequestHeader.Cookie, "cookiename1 = cookievalue1; cookiename2 = cookievalue2");
Ian Kemp
46

Yang ini hanyalah perpanjangan dari artikel yang Anda temukan.


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
sumber
3
Ini bekerja dengan baik @Pavel, meskipun Anda dapat meningkatkan jawaban ini dengan menunjukkan cara menggunakan fitur-fitur kelas, terutama pengaturan dan mendapatkan cookie di atasnya.
Corgalore
Terima kasih untuk ekstensi. Untuk menggunakannya saya menambahkan CookieContainer publik CookieContainer {get {return _container; } set {_container = value; }}
Igor Shubin
1
@IgorShubin Anda harus menghapus readonlypengubah containerbidang, jika tidak, Anda tidak dapat mengaturnya di properti. Saya memodifikasi kodenya.
hillin
1
Tidakkah Anda harus memeriksa Set-Cookietajuk respons di ReadCookies?
Achilles
2
Anda sebenarnya tidak memerlukan GetWebResponsedan ReadCookies, karena cookie akan ditambahkan ke wadah secara otomatis.
lionello
15

HttpWebRequest memodifikasi CookieContainer yang ditugaskan padanya. Tidak perlu memproses cookie yang dikembalikan. Cukup tetapkan wadah cookie Anda untuk setiap permintaan 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
sumber
6

Saya pikir ada cara yang lebih bersih di mana Anda tidak harus membuat klien web baru (dan itu akan bekerja dengan perpustakaan pihak ke-3 juga)

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

Sekarang yang harus Anda lakukan adalah memilih di mana Anda ingin menggunakan domain ini:

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

Itu berarti SETIAP permintaan web yang masuk ke example.com sekarang akan menggunakan pembuat permintaan web kustom Anda, termasuk klien web standar. Pendekatan ini berarti Anda tidak perlu menyentuh semua kode Anda. Anda cukup memanggil awalan register sekali dan selesai dengan itu. Anda juga dapat mendaftar untuk awalan "http" untuk ikut serta dalam segala hal di mana pun.

dotMorten
sumber
Saya tidak yakin tentang beberapa kalimat terakhir; yang docs mengatakan: "Kelas HttpWebRequest terdaftar untuk permintaan layanan untuk skema HTTP dan HTTPS secara default Upaya untuk mendaftarkan keturunan WebRequest berbeda untuk skema ini akan gagal.."
Herohtar