zoukankan      html  css  js  c++  java
  • ASP.NET Core 2.2 基础知识(十二) 发送 HTTP 请求

    可以注册 IHttpClientFactory 并将其用于配置和创建应用中的 HttpClient 实例。 这能带来以下好处:

    • 提供一个中心位置,用于命名和配置逻辑 HttpClient 实例。 例如,可以注册 github 客户端,并将它配置为访问 GitHub。 可以注册一个默认客户端用于其他用途。
    • 通过委托 HttpClient 中的处理程序整理出站中间件的概念,并提供适用于基于 Polly 的中间件的扩展来利用概念。
    • 管理基础 HttpClientMessageHandler 实例的池和生存期,避免在手动管理 HttpClient 生存期时出现常见的 DNS 问题。
    • (通过 ILogger)添加可配置的记录体验,以处理工厂创建的客户端发送的所有请求。

    在应用中可以通过以下多种方式使用 IHttpClientFactory

    基本用法

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
                services.AddHttpClient();
            }
        [Route("api/[controller]")]
        [ApiController]
        public class ValuesController : ControllerBase
        {
            private readonly IHttpClientFactory _clientFactory;
            public ValuesController(IHttpClientFactory clientFactory)
            {
                _clientFactory = clientFactory;
            }
    
            // GET api/values
            [HttpGet]
            public async Task<string> Get()
            {
                HttpClient client = _clientFactory.CreateClient();
    
                //方法一:
                //HttpRequestMessage request = new HttpRequestMessage
                //{
                //    Method = new HttpMethod("get"),
                //    RequestUri = new System.Uri("http://localhost:5000/api/values"),
                //};
                //HttpResponseMessage response = await client.SendAsync(request);
                //string res = await response.Content.ReadAsStringAsync();
                //return res;
    
                //方法二:
                string res = await client.GetStringAsync("http://localhost:5000/api/values");
                return res;
            }
         }

    命名客户端

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
                services.AddHttpClient("test", c =>
                {
                    c.BaseAddress = new Uri("http://localhost:5000");
                });
            }
            public async Task<string> Get()
            {
                HttpClient client = _clientFactory.CreateClient("test");
                //注册名叫 "test" 的客户端时,已经指定了该客户端的请求基地址,所以这里不需要指定主机名了
                return await client.GetStringAsync("api/values");
            }

    类型化客户端

        public class TestHttpClient
        {
            public HttpClient Client { get; set; }
    
            public TestHttpClient(HttpClient client)
            {
                client.BaseAddress = new System.Uri("http://localhost:5000");
                Client = client;
            }
    
            public async Task<string> Get()
            {
                return await Client.GetStringAsync("api/values");
            }
        }
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
                services.AddHttpClient<TestHttpClient>(c =>
                {
                    //可以在这里设置,也可以在构造函数设置.
                    //c.BaseAddress = new System.Uri("http://localhost:5000");
                });
            }
        [Route("api/[controller]")]
        [ApiController]
        public class ValuesController : ControllerBase
        {
            private readonly TestHttpClient _client;
    
            public ValuesController(TestHttpClient client)
            {
                _client = client;
            }
         
            [HttpGet]
            public async Task<string> Get()
            {
                return await _client.Get();
            }
        }

    出站请求中间件

    HttpClient 已经具有委托处理程序的概念,这些委托处理程序可以链接在一起,处理出站 HTTP 请求。 IHttpClientFactory 可以轻松定义处理程序并应用于每个命名客户端。 它支持注册和链接多个处理程序,以生成出站请求中间件管道。 每个处理程序都可以在出站请求前后执行工作。 此模式类似于 ASP.NET Core 中的入站中间件管道。 它提供了一种用于管理围绕 HTTP 请求的横切关注点的机制,包括缓存、错误处理、序列化以及日志记录。

    要创建处理程序,需要定义一个派生自 DelegatingHandler 的类。 重写 SendAsync 方法,在将请求传递至管道中的下一个处理程序之前执行代码:

        public class ValidateHeaderHandler : DelegatingHandler
        {
            protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                if (!request.Headers.Contains("refuge"))
                {
                    return new HttpResponseMessage(HttpStatusCode.BadRequest)
                    {
                        Content = new StringContent("not found refuge")
                    };
                }
    
                return await base.SendAsync(request, cancellationToken);
            }
        }
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
                services.AddTransient<ValidateHeaderHandler>();//生存期必须是临时
                services.AddHttpClient("test", c =>
                    {
                        c.BaseAddress = new Uri("http://localhost:5000");
                    })
                    .AddHttpMessageHandler<ValidateHeaderHandler>();
            }

    HttpClient 和生存期管理

    每次对 IHttpClientFactory 调用 CreateClient 都会返回一个新 HttpClient 实例:

            public IEnumerable<int> Get()
            {
                //测试生存期
                for (int i = 0; i < 4; i++)
                {
                    HttpClient client = i % 2 == 0
                        ? _clientFactory.CreateClient("test")
                        : _clientFactory.CreateClient();
                    yield return client.GetHashCode();
                }
            }

    CreateClient 方法内部会调用 CreateHandler 方法,后者创建 HttpMessageHandler,

    源码如下:

        public HttpClient CreateClient(string name)
        {
          if (name == null)
            throw new ArgumentNullException(nameof (name));
          HttpClient httpClient = new HttpClient(this.CreateHandler(name), false);
          HttpClientFactoryOptions clientFactoryOptions = this._optionsMonitor.Get(name);
          for (int index = 0; index < clientFactoryOptions.HttpClientActions.Count; ++index)
            clientFactoryOptions.HttpClientActions[index](httpClient);
          return httpClient;
        }
        public HttpMessageHandler CreateHandler(string name)
        {
          if (name == null)
            throw new ArgumentNullException(nameof (name));
          ActiveHandlerTrackingEntry entry = this._activeHandlers.GetOrAdd(name, this._entryFactory).Value;
          this.StartHandlerEntryTimer(entry);
          return (HttpMessageHandler) entry.Handler;
        }

    而这个 _activeHandlers 的类型是 : 

    一个线程安全的键值对集合.

    因此,实际上创建的 HttpMessageHandler 实例会汇集到池中.新建 HttpClient 实例时,可能会重用池中的 HttpMessageHandler 实例(如果生存期尚未到期的话). 

    由于每个处理程序通常管理自己的基础 HTTP 连接,因此需要池化处理程序.创建超出必要数量的处理程序可能会导致连接延迟. 部分处理程序还保持连接无期限地打开,这样可以防止处理程序对 DNS 更改作出反应.

    处理程序的默认生存期为两分钟,可在每个命名客户端上重写默认值:

    services.AddHttpClient("test").SetHandlerLifetime(TimeSpan.FromMinutes(5));

    配置 HttpMessageHandler

    有时候,我们需要控制客户端使用的内部 HttpMessageHandler .

                services.AddHttpClient("test")
                    .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler()
                    {
                        AllowAutoRedirect = false,
                    });
  • 相关阅读:
    Excel设置下拉选项的方法
    Codeforces Round #218 (Div. 2) (线段树区间处理)
    手动配置S2SH三大框架报错(一)
    一种H.264高清视频的无参考视频质量评价算法(基于QP和跳过宏块数)
    UIWebView的使用
    AFNetworkIng的简单使用
    虚线边框的实现
    iOS实现简单时钟效果
    hdu 3966 Aragorn's Story
    Count on a tree
  • 原文地址:https://www.cnblogs.com/refuge/p/10230795.html
Copyright © 2011-2022 走看看