.Net 4.5中的HttpClient試用

.Net 4.5中的HttpClient試用

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

基本操做

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

static async Task<string> GetData()
{
    var httpClient = new HttpClient();
    return await httpClient.GetStringAsync("http://www.weather.com.cn/data/sk/101010100.html");
}
        
        
static  void Main(string[] args)
{
    Console.WriteLine(GetData().Result);
    Console.ReadLine();
}

它支持編碼識別和對壓縮的http流解壓,省去了咱們的很多代碼。除GetStringAsync()以外,還有GetByteArrayAsync()、GetStreamAsync()、PostAsync ()、DeleteAsync()等函數,很是好用。windows

注意:HttpClient提供的函數基本都是異步的api

HttpClient.GetStringAsync()是一個簡化的函數,用這個函數的時候,咱們看不到HttpResponse的相關信息,若是須要看到Http響應的信息,能夠用以下標準方式:安全

static async Task<string> GetData()
{
    var httpClient = new HttpClient();
    var httpResponMassage=await httpClient.GetAsync("http://www.weather.com.cn/data/sk/101010100.html");
    //請求成功
    if (httpResponMassage.IsSuccessStatusCode)
    {
        return await httpResponMassage.Content.ReadAsStringAsync();
    }
    return null;
}

自定義HttpHeader

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

  1. 首先自定義一個HttpClienHanlder類,重載SendAsync方法。
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    public class MyHttpClientHandler:HttpClientHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            //告訴請求的api地址我是來自百度的調整過來的請求
            request.Headers.Referrer=new Uri("http://www.baidu.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傳入該自定義對象
static async Task<string> GetData()
{
    var client= new HttpClient(new MyHttpClientHandler());
    return await client.GetStringAsync("http://www.weather.com.cn/data/sk/101010100.html");
}

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

常見問題

HttpClient雖然很是簡單易用,但並不意味着它任什麼時候候都能照着咱們指望的方式工做,常見問題(我這兩天試用過程當中遇到的)以下:ide

  1. 中文亂碼。

通常發生這種狀況是因爲頁面的charset沒有設置爲「utf-8」或「GBK」編碼格式。
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信息,甚至經過相應的庫函數進行編碼猜想。這兒我寫了一個稍微完善的版本:

class HtmlTextHandler : HttpClientHandler
    {
        protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var response = await base.SendAsync(request, cancellationToken);

            var contentType = response.Content.Headers.ContentType;
            contentType.CharSet = await getCharSetAsync(response.Content);

            return response;
        }

        private async Task<string> getCharSetAsync(HttpContent httpContent)
        {
            var charset = httpContent.Headers.ContentType.CharSet;
            if (!string.IsNullOrEmpty(charset))
                return charset;

            var content = await httpContent.ReadAsStringAsync();
            var match = Regex.Match(content, @"charset=(?<charset>.+?)""", RegexOptions.IgnoreCase);
            if (!match.Success)
                return charset;

            return match.Groups["charset"].Value;
        }
    }
  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()來等待任務完成,稍稍有些不便。

閱讀原文

相關文章
相關標籤/搜索