zoukankan      html  css  js  c++  java
  • .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()来等待任务完成,稍稍有些不便。

  • 相关阅读:
    react ts axios 配置跨域
    npm run eject“Remove untracked files, stash or commit any changes, and try again.”错误
    java 进程的参数和list的线程安全
    帆软报表 大屏列表跑马灯效果JS
    帆软报表 快速复用数据集,避免重复劳动
    分析云 OA中部门分级思路和实现方法
    分析云 分段器 只显示一个块的数据
    分析云 更改服务默认的端口号
    分析云U8项目配置方法新版本(2)
    Oracle 创建时间维度表并更新是否工作日字段
  • 原文地址:https://www.cnblogs.com/TianFang/p/2389480.html
Copyright © 2011-2022 走看看