.Net 4.5中的HttpClient試用


.Net 4.5中增加了一個新的System.Net.Http.HttpClient名字空間(在 System.Net.Http.dll 中),用於發送 HTTP 請求和接收 HTTP 響應。

基本操作

和以前的HttpWebRequest相比,HttpClient更加簡潔,下面就是一個下載www.windows.com頁面的示例:

    string uri = "http://www.windows.com/";
    HttpClient client = new HttpClient();
    string body = await client.GetStringAsync(uri);

它支持編碼識別和對壓縮的http流解壓,省去了我們的不少代碼。除GetStringAsync()之外,還有GetByteArrayAsync()GetStreamAsync()PostAsync ()DeleteAsync()等函數,非常好用。

HttpClient.GetStringAsync()是一個簡化的函數,用這個函數的時候,我們看不到HttpResponse的相關信息,如果需要看到Http響應的信息,可以用如下標准方式:

    HttpResponseMessage response = await client.GetAsync(uri);
    response.EnsureSuccessStatusCode();
    string responseBody = await response.Content.ReadAsStringAsync();

自定義HttpHeader

前面的示例非常簡單,但有時我們需要在發送Get請求時在HttpHeader中加入一些額外的信息,常見的的有Refer、Cookie及UserAgent等。這個時候我們就要用到HttpClientHandler了,具體方法如下:

  1. 首先自定義一個 HttpClienHanlder 類,重載SendAsync方法。

    string uri = "http://www.windows.com/";
    HttpClient client = new HttpClient();

    class MyHttpClienHanlder:HttpClientHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            request.Headers.Referrer = new Uri("http://www.google.com/");
            request.Headers.Add("UserAgent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727)");

            return base.SendAsync(request, cancellationToken);
        }
    }

  1. HttpClient 構造函數中加入自定義的 HttpClienHanlder 類。

    string uri = "http://www.windows.com/";
    HttpClient client = new HttpClient(new MyHttpClienHanlder());
    string body = await client.GetStringAsync(uri);

可見,HttpClienHanlder其實就是是一個常見的代理模式的設計,它在HttpClient.GetStringAsync()中加了一層封裝,攔截了HttpClient的輸入和輸出,從而實現一些自定義的操作。

常見問題

HttpClient雖然非常簡單易用,但並不意味着它任何時候都能照着我們期望的方式工作,常見問題(我這兩天試用過程中遇到的)如下:

  1. 中文亂碼

HttpClient.GetStringAsync()本身支持編碼識別,但如果HttpResponse的HttpHeader中不含CharSet信息時,便采用默認編碼方式進行字符串解碼,它的默認編碼方式是無法解析中文的,此時便會出現中文亂碼。

一種常見的做法是:如果HttpHeader中不含CharSet信息時,采用GBK方式來解碼。要實現這個功能的話,還是需要用到前面提到的HttpClientHandler

    class MyHttpClienHanlder:HttpClientHandler
    {
        protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var rsponse = await base.SendAsync(request, cancellationToken);
            var contentType = rsponse.Content.Headers.ContentType;
            if (string.IsNullOrEmpty(contentType.CharSet))
            {
                contentType.CharSet = "GBK";
            }
            return rsponse;
        }
    }

當然,這么做仍然不是很完善,有的時候如果要更精確的話還需要從Html頁面中獲取charset信息,甚至通過相應的庫函數進行編碼猜測。這兒我寫了一個稍微完善的版本:

 1     class HtmlTextHandler : HttpClientHandler
 2     {
 3         protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 4         {
 5             var response = await base.SendAsync(request, cancellationToken);
 6 
 7             var contentType = response.Content.Headers.ContentType;
 8             contentType.CharSet = await getCharSetAsync(response.Content);
 9 
10             return response;
11         }
12 
13         private async Task<string> getCharSetAsync(HttpContent httpContent)
14         {
15             var charset = httpContent.Headers.ContentType.CharSet;
16             if (!string.IsNullOrEmpty(charset))
17                 return charset;
18 
19             var content = await httpContent.ReadAsStringAsync();
20             var match = Regex.Match(content, @"charset=(?<charset>.+?)""", RegexOptions.IgnoreCase);
21             if (!match.Success)
22                 return charset;
23 
24             return match.Groups["charset"].Value;
25         }
26     }
View Code
  1. 響應內容過長導致HttpRequestException

HttpClient有一個屬性MaxResponseContentBufferSize,它表示的是讀取響應內容時最大字節數緩沖區。它的默認值是64k,當頁面內容很多,超過64k的時候,就會拋出一個HttpRequestException,導致Get失敗。

這個屬性必須是個正整數,也就是說,它是不支持自適應的,這個非常令人費解,不知道MS為什么非要自己估算頁面大小,在Get操作前支持為合適的值,這個是個不夠好用的地方。

我查了一下MSDN,目前對這個屬性的說明比較少,不知道更改這個值的大小會影響什么地方。即使把他設置成int.Max貌似也不會有過多的內存占用。不過為了安全起見,還是把它設置在一個合理的范圍吧,像我一般就把它設置為1m。(PS: 在最新的.Net 4.5 RC中,這個值已經更新成了int.MaxValue,希望RTM版不要恢復成64k,確實不夠用)

    HttpClient client = new HttpClient() { MaxResponseContentBufferSize = 1024 * 1024 }; 

最后提一個不是問題的問題:HttpClient全部都是異步方法,沒有同步方法,如果要在同步函數中使用,必須通過Task.Wait()來等待任務完成,稍稍有些不便。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM