我們都知道 WebClient 類別是個簡單易用的東西,不只可以用作 HTTP 用途,連 FTP 都能用,想偷懶時很快就能寫出一些網路資料上傳、下載的程式,像我在寫一些測試程式時經常會使用 WebClient 類別,但大多情況都用來「下載網頁」居多,少有模擬表單上傳資料的情況,但利用 WebClient 類別在「傳送表單資料」時要小心使用,否則遠端接不到資料又很難除錯時哪就麻煩了。
與「上傳資料」有關的方法有以下四種 (不考慮非同步模式的方法)
如果是你,你會選用哪一種呢?你會不會覺得 string 比較友善呢? ^__^
像我們之前就這樣寫,以為可以順利的將資料上傳(錯誤示範):
using (WebClient wc = new WebClient())
{
try
{
wc.Encoding = Encoding.UTF8;
string resultXML = wc.UploadString(Config.PostURL,
String.Format("id={0}&pw={1}", user.ID, user.PW));
}
catch (WebException ex)
{
throw new Exception("無法連接遠端伺服器");
}
}
透過這段程式所送出的 HTTP Request Header 如下:
POST /Home/Echo HTTP/1.1
Host: my.example.com:52215
Content-Length: 13
Connection: Keep-Alive
id=aaa&pw=bbb
雖然一樣是 POST 方法,但是缺少了最重要的 Content-Type 標頭,也就是:
Content-Type: application/x-www-form-urlencoded
以我們常用 ASP.NET MVC 為例,你不傳送標準的 Content-Type 過去,就無法正確抓到所有表單資料,也無法透過 Model Binder 自動繫結到 Action 參數中!
雖然你可以利用 wc.Headers 屬性自行將缺少的 Header 加上去,例如:
wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
但後來我們改用 UploadValues 方法,程式碼範例如下:
using (WebClient wc = new WebClient())
{
try
{
wc.Encoding = Encoding.UTF8;
NameValueCollection nc = new NameValueCollection();
nc["id"] = "aaa";
nc["pw"] = "bbb";
byte[] bResult = wc.UploadValues(Config.PostURL, nc);
string resultXML = Encoding.UTF8.GetString(bResult);
}
catch (WebException ex)
{
throw new Exception("無法連接遠端伺服器");
}
}
雖然多了幾行 Code,但是其實語法更容易理解,而且也不用自己組 POST 資料中類似 QueryString 的字串,還會自動做 URLEncode 動作,只是最後多一個步驟要將 byte[] 轉回 string 而已,這段程式最後會送出的 HTTP Request Header 如下:
POST /Home/Echo HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: my.example.com:52215
Content-Length: 13
Connection: Keep-Alive
id=aaa&pw=bbb
但是,使用 WebClient 類別其實有許多缺點,所以我文章一開頭就說過「想偷懶時」,主要的缺點有:
- 無法指定 Timeout,所以當網路連不上時,網頁或程式會整個停在那裡很久!
- 不適合用來下載大量的檔案,高負載的網站也不適合這樣用,即便你用非同步的方式撰寫,也會讓 WebClient 因為佔據過多 Threads 而導致效能降低,壹台電腦的 Threads 數是有限制的。
對於較正式的場合,還是建議改用 HttpWebRequest 類別處理。
相關連結