.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; }
前面的示例很是简单,但有时咱们须要在发送Get请求时在HttpHeader中加入一些额外的信息,常见的的有Refer、Cookie及UserAgent等。这个时候咱们就要用到HttpClientHandler了,具体方法以下:异步
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); } } }
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
通常发生这种状况是因为页面的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; } }
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()来等待任务完成,稍稍有些不便。