参考:
https://blog.csdn.net/u010476739/article/details/119782562(HttpClint使用案例)

HttpClient用于发送http请求,默认情况下内部使用HttpWebRequest发送请求,这一行为可以通过构造函数中指定HttpMessageHandler参数来使用不同的通道
HttpClient构造函数可以接受一个HttpMessageHandler类型的参数
HttpClient继承自HttpMessageInvoker,从名字可以看出HttpClient是一个调用器,它是对HttpMessageHandler的一个调用,HttpClient本身不提供Http请求功能

public HttpClient(HttpMessageHandler handler){}

HttpClient相关类

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

HttpClientHandler

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

  • 是否自动处理cookie
    默认HttpClient是自动处理cookie的,即:上一个请求返回的cookie,可能会随着下次请求发送出去。
            HttpClientHandler httpClientHandler = new HttpClientHandler { 
                UseCookies=false//是否自动处理cookie
            };
            HttpClient client = new HttpClient(httpClientHandler);
  • 是否自动重定向以及最多重定向几次
    默认HttpClient自动处理重定向请求,并且最多重定向50次
HttpClientHandler httpClientHandler = new HttpClientHandler
            {
                AllowAutoRedirect = true,//是否允许重定向,默认为true
                MaxAutomaticRedirections = 50//最多重定向几次,默认50次
            };
  • 内部TCP链接池的设置
    每个url(如:http://www.baidu.com:80)最多有几个链接,默认是int.MaxValue。注意:url是不带路径及参数;
            HttpClientHandler httpClientHandler = new HttpClientHandler
            {
                MaxConnectionsPerServer=int.MaxValue, 
            };
  • 是否压缩
    默认是不压缩,如果设置开启压缩的话,http请求头中会自动加上Accept-Encoding: gzip(当然你得设置压缩选项是gzip),如果后台也支持这种压缩的话,就会把消息体压缩并在响应头中添加Content-Encoding: gzip
            HttpClientHandler httpClientHandler = new HttpClientHandler
            {
                AutomaticDecompression=System.Net.DecompressionMethods.GZip
            };
  • 超时设置
            SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler { 
               ConnectTimeout= System.Threading.Timeout.InfiniteTimeSpan//默认不限制
            };
  • 响应头数据大小限制
    http响应头最大字节数
            HttpClientHandler httpClientHandler = new HttpClientHandler
            {
                MaxResponseHeadersLength = 64,//单位是KB
            };

验证相关:PreAuthenticate、Credentials
代理相关:Proxy、UseProxy、DefaultProxyCredentials

DelegatingHandler

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

public abstract class DelegatingHandler : HttpMessageHandler
{}

HttpClient请求过程

从Request发起,经过DelegatingHanlder处理后,进入InnerHandler,数据返回后再从Inner Handler 返回到Delegating Hanlder进行处理,最后返回结果。
从设计角度来讲,HttpClient库提供了强大的扩展性,使用者不需要任何继承即可完成对HttpClient的扩展(如果对设计模式熟悉,可以清楚的看出这里用到了装饰器模式)
深入理解HttpClient

HttpResponseMessage

HttpClient请求的返回类型,内部包含Headlers、Content、StatusCode、IsSuccessStatusCode等属性
EnsureSuccessStatusCode()方法:如果不返回200就抛出异常
其中Content属性是HttpContent类型,可转成对应类型,获取Content数据
深入理解HttpClient

与Refit结合

https://www.xcode.me/code/refit-the-automatic-type-safe-rest-library
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.1#outgoing-request-middleware

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抽象类实现

我们自己定义了一个LoggingHandler,这个类对应Delegating Handler 是我们自定义的、装饰在Inner Handler外的Handler。
DelegatingHandler重载了SendAsync,在其内部调用了InnerHandler的SendAsync方法,如此我们便可以在实际请求发出,以及返回后进行各种统一的处理,总结起来仍是上面图中画出的,逐层调用。
必须给LoggingHandler传入HttpClientHandler,因为最终都是通过HttpClientHandler发送请求,如果不传会抛异常

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));

案例3:失败重试

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);

案例: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
                });
        }

HttpClientFactory使用案例

展开代码

注册

   public void ConfigureServices(IServiceCollection services)
   {
    //1、
    services.AddHttpClient();
    //2、命名化客户端,可以添加一些特定配置。您可以注册多个命名化客户端,每个客户端都可以预先配置不同的设置。
       services.AddHttpClient("client_1",config=> 
       {
           config.BaseAddress= new Uri("http://client_1.com");
           config.DefaultRequestHeaders.Add("header_1","header_1");
       });
    //3、类型化客户端,此HttpClient可以注入到一个自定义类中
        services.AddHttpClient<SSOService>();
   }
    //类型化客户端类
    public class SSOService
    {
        private readonly HttpClient _httpClient = null;
        public SSOService(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }
        public string GetContent()
        {
            return _httpClient.GetAsync("https://www.baidu.com").Result.Content.ReadAsStringAsync().Result;
        }
    }

使用HttpClient

 public async Task<ActionResult> Test([FromServices]IHttpClientFactory httpClientFactory,[FromServices] SSOService ssoService)
 {
     var client1 = httpClientFactory.CreateClient(); 
     var result1 = await client1.GetStringAsync("http://www.site.com/XXX.html");

     var client2 = httpClientFactory.CreateClient("client_1"); 
     var result2 = await client2.GetStringAsync("/page1.html");

     var result3= ssoService.GetContent();

     return Content();
 }

HttpClient+Consule案例

展开代码
public class ConsulDiscoveryDelegatingHandler : DelegatingHandler
{
     protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
     {
          var current = request.RequestUri;
          try
          {              //调用的服务地址里的域名(主机名)传入发现的服务名称即可
              request.RequestUri = new Uri($"{current.Scheme}://{LookupService(current.Host)}/{current.PathAndQuery}");
              return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
          }
          catch (Exception e)
          {
              throw;
          }
          finally
          {
              request.RequestUri = current;
          }
      }

      private string LookupService(string serviceName)
      {
          using (ConsulClient consulClient = new ConsulClient(config=>config.Address=new Uri("http://localhost:8500/")))
          {
              var services = _consulClient.Catalog.Service(serviceName).Result.Response;
              if (services != null && services.Any())
              {
                    //模拟负载均衡算法(随机获取一个地址)
                    int index = r.Next(services.Count());
                    var service = services.ElementAt(index);
                    return $"{service.ServiceAddress}:{service.ServicePort}");
               }
               return null;
          }
      }}

调用

public async Task<Person> GetPerson(int personId)
{
    using (HttpClient client = new HttpClient(new ConsulDiscoveryDelegatingHandler()))
    {       //调用时的域名(主机名)传入服务发现的名称即可
       var response = await client.GetAsync($"http://PersonService/Person/GetPerson?personId={personId}");
       var jsonResult = await response.Content.ReadAsStringAsync();
       return jsonResult.FromJson<Person>();
    }
}

相关文章:

  • 2021-07-16
  • 2021-11-17
  • 2021-05-17
  • 2021-05-22
  • 2021-10-02
  • 2021-10-17
  • 2021-12-12
  • 2021-08-05
猜你喜欢
  • 2022-12-23
  • 2022-01-24
  • 2021-10-30
  • 2023-03-08
  • 2021-09-08
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案