我們都知道 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 類別處理。
相關連結