zoukankan      html  css  js  c++  java
  • 深入理解HttpClient

    介绍:

    HttpClient用于发送http请求,默认情况下内部使用HttpWebRequest发送请求,这一行为可以通过构造函数中指定HttpMessageHandler参数来使用不同的通道

    HttpClient这个类本身并不会进行实际的网络请求收发处理,我们应将其理解成一个容器、一个中继者,实际的网络请求核心在HttpClientHanlder中,也就是下面图中对应的Inner Handler。

    HttpClient构造函数可以接受一个HttpMessageHandler类型的参数

    HttpClientHandler:HttpClientHandler继承自HttpMessageHandler,用于设置cookie、代理、证书、Headlers等属性

    DelegatingHandler:抽象类,DelegatingHandler继承自HttpMessageHandler,DelegatingHandler内部有一个属性InnerHandler,http请求都是通过InnerHandler发送。通过继承此类可实现类似HttpModule的功能

    HttpClient继承自HttpMessageInvoker,从名字可以看出HttpClient是一个调用器,它是对HttpMessageHandler的一个调用,HttpClient本身不提供Http请求功能

    HttpClient请求过程:

    从Request发起,经过DelegatingHanlder处理后,进入InnerHandler,数据返回后再从Inner Handler 返回到Delegating Hanlder进行处理,最后返回结果。

    从设计角度来讲,HttpClient库提供了强大的扩展性,使用者不需要任何继承即可完成对HttpClient的扩展(如果对设计模式熟悉,可以清楚的看出这里用到了装饰器模式)

    Httpclient的设计图:

       

    案例1:

    设置cookie、Headers、代理、证书等,通过设置HttpClientHandler属性实现

    HttpClientHandler httpClientHandler = new HttpClientHandler();
                httpClientHandler.CookieContainer.Add(new Cookie("id", "1"));
                //httpClientHandler.Proxy = null;
                //httpClientHandler.Credentials = null;
                HttpClient httpClient = new HttpClient(httpClientHandler);

    案例2:日志

    将HttpClient请求、响应内容记录日志,通过继承DelegatingHandler抽象类实现

    public class LoggingHandler : DelegatingHandler
        {
            public LoggingHandler(HttpMessageHandler handler) : base(handler) { }
            protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                if (request.Content!=null)
                {
                    Debug.WriteLine(await request.Content.ReadAsStringAsync());
                }
                HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
                if (response.Content!=null)
                {
                    Debug.WriteLine(await response.Content.ReadAsStringAsync());
                }
                return response;
            }
        }
    HttpMessageHandler handler = new HttpClientHandler();
    HttpClient client = new HttpClient(new LoggingHandler(handler));

    代码说明:

    1、HttpClient这个类本身并不会进行实际的网络请求收发处理,我们应将其理解成一个容器、一个中继者,实际的网络请求核心在HttpClientHanlder中,也就是前面图中对应的Inner Handler。

    2、我们自己定义了一个LoggingHandler,这个类对应Delegating Handler 是我们自定义的、装饰在Inner Handler外的Handler。

    3、DelegatingHandler重载了SendAsync,在其内部调用了InnerHandler的SendAsync方法,如此我们便可以在实际请求发出,以及返回后进行各种统一的处理,总结起来仍是上面图中画出的,逐层调用。

    4、HttpClientHandler、DelegatingHandler都是继承自HttpMessageHandler

    5、必须给LoggingHandler传入HttpClientHandler,因为最终都是通过HttpClientHandler发送请求,如果不传会抛异常

    HttpClientHandler:

    public class HttpClientHandler : HttpMessageHandler
    {}

    DelegatingHandler:

    public abstract class DelegatingHandler : HttpMessageHandler
    {}

    案例3:重试

    封装一个RetryHandler,目的是失败重试

    RetryHandler:

        public class RetryHandler : DelegatingHandler
        {
            private const int MAX_COUNT = 3;
            protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                HttpResponseMessage response = null;
                for (int i = 0; i < MAX_COUNT; i++)
                {
                    response = await base.SendAsync(request, cancellationToken);
                    if (response.IsSuccessStatusCode)
                    {
                        return response;
                    }
                }
                return response;
                
            }
        }
    HttpMessageHandler handler = new HttpClientHandler();
    handler = new LoggingHandler(handler);
    handler = new RetryHandler(handler);
    HttpClient client = new HttpClient(handler);

    案例4:ASP.NET Core中指定DelegatingHandler、HttpClientHandler

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddHttpClient("client1", client => { client.BaseAddress = new System.Uri("https://www.baidu.com"); })
                    .AddHttpMessageHandler(() => new RetryHandler())//设置DelegatingHandler
                    .ConfigurePrimaryHttpMessageHandler(() => new System.Net.Http.HttpClientHandler()
                    {//设置HttpClientHandler,也就是InnerHandler
                        UseCookies = false
                    });
            }

    HttpResponseMessage:

    HttpClient请求的返回类型,内部包含Headlers、Content、StatusCode、IsSuccessStatusCode等属性

    其中Content属性是HttpContent类型,可转成对应类型,获取Content数据

    性能隐患:

    HttpClient有一个性能隐患,当要发送大量的http请求,如果频繁的创建HttpClient对象,会频繁建立TCP连接,连接不够用时会出现timeout,所以应该避免频繁创建HttpClient对象,如下使用静态变量+长连接。

    private static readonly HttpClient _httpClient;
    
            static ApiLogger()
            {
                _httpClient = new HttpClient();
                _httpClient.Timeout = new TimeSpan(0, 0, 10);
                _httpClient.DefaultRequestHeaders.Connection.Add("keep-alive");
            }

    上面这种方式有一个缺点,会缓存Dns,所以.net core中应该使用HttpClientFactory创建HttpClient

    HttpClient扩展方法:

    public static class HttpClientEx 
        {
            //GZIP压缩
            //var handler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip }
            //HttpClient client = new HttpClient(handler);
    
            /// <summary>
            /// http://www.it165.net/pro/html/201306/6052.html
            /// GET获取HTTP信息
            /// </summary>
            /// <param name="url"></param>
            /// <returns></returns>
            public static string Get(this HttpClient client,string url)
            {
                return client.GetStringAsync(url).Result;
            }
            /// <summary>
            /// POST空的HTTP信息
            /// http://www.cnblogs.com/xishuai/p/aspnet_mvc_web_api_httpclient_json_frombody_post.html
            /// </summary>
            /// <param name="url"></param>
            /// <returns></returns>
            public static string Post(this HttpClient client,string url)
            {
                string responseText = null;
                using (StringContent content = new StringContent(string.Empty))
                {
                    content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
                    using (HttpResponseMessage responseMessage = client.PostAsync(url, content).Result)
                    {
                        responseMessage.EnsureSuccessStatusCode();//确保请求成功
                        responseText = responseMessage.Content.ReadAsStringAsync().Result;
    
                    }
                }
                return responseText;
            }
            /// <summary>
            /// http://www.haogongju.net/art/1642652
            /// POST方式发送信息(x-www-form-urlencoded字典格式)
            /// </summary>
            /// <param name="url"></param>
            /// <param name="dict">x-www-form-urlencoded字典格式</param>
            /// <param name="isException">false表示抛出异常,true表示返回异常错误信息</param>
            /// <returns></returns>
            public static string Post(this HttpClient client,string url, Dictionary<string, string> dict)
            {
                string responseText = null;
                using (FormUrlEncodedContent content = new FormUrlEncodedContent(dict))
                {
                    //content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
                    using (HttpResponseMessage responseMessage = client.PostAsync(url, content).Result)
                    {
                        responseMessage.EnsureSuccessStatusCode();
                        responseText = responseMessage.Content.ReadAsStringAsync().Result;
    
                    }
                }
                return responseText;
            }
            /// <summary>
            /// http://www.cnblogs.com/xishuai/p/aspnet_mvc_web_api_httpclient_json_frombody_post.html
            /// POST方式发送信息(raw文本格式)
            /// </summary>
            /// <param name="url"></param>
            /// <param name="raw">raw文本格式</param>
            /// <param name="isException">false表示抛出异常,true表示返回异常错误信息</param>
            /// <returns></returns>
            public static string Post(this HttpClient client,string url, string raw)
            {
                string responseText = null;
                using (StringContent content = new StringContent(raw))
                {
                    content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                    using (HttpResponseMessage responseMessage = client.PostAsync(url, content).Result)
                    {
                        responseMessage.EnsureSuccessStatusCode();
                        responseText = responseMessage.Content.ReadAsStringAsync().Result;
    
                    }
                }
                return responseText;
            }
    
            
    
        }

    在.NET Core中,如果使用HttpClientFactory创建HttpClient,如何添加HttpMessageHandler?

    需要自己去实现接口IHttpMessageHandlerBuilderFilter,看下一篇

    参考:

    https://www.cnblogs.com/Herzog3/p/6128822.html

    https://www.cnblogs.com/Leo_wl/p/3439512.html

    https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers

    https://blog.csdn.net/weixin_30236595/article/details/101066107

    ...

  • 相关阅读:
    day 66 crm(3) 自创组件stark界面展示数据
    day 65 crm(2) admin源码解析,以及简单的仿造admin组件
    用 Python+nginx+django 打造在线家庭影院
    django -admin 源码解析
    day 64 crm项目(1) admin组件的初识别以及应用
    云链接 接口不允许 情况 解决方法 mysql Host is not allowed to connect to this MySQL server解决方法
    day 56 linux的安装python3 ,虚拟环境,mysql ,redis
    day55 linux 基础以及系统优化
    Codeforces 989 P循环节01构造 ABCD连通块构造 思维对云遮月参考系坐标轴转换
    Codeforces 990 调和级数路灯贪心暴力 DFS生成树两子树差调水 GCD树连通块暴力
  • 原文地址:https://www.cnblogs.com/fanfan-90/p/12409101.html
Copyright © 2011-2022 走看看