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

原文地址:https://www.cnblogs.com/refuge/p/10230795.html

时间: 2024-10-17 11:45:38

ASP.NET Core 2.2 基础知识(十二) 发送 HTTP 请求的相关文章

ASP.NET Core 2.2 基础知识(十六) SignalR 概述

原文:ASP.NET Core 2.2 基础知识(十六) SignalR 概述 我一直觉得学习的最好方法就是先让程序能够正常运行,才去学习他的原理,剖析他的细节. 就好像这个图: 所以,我们先跟着官方文档,创建一个 SignalR 应用: https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?view=aspnetcore-2.2&tabs=visual-studio 这个例子一共涉及到下面几个步骤: 自定义中心 ChatH

ASP.NET Core 2.2 基础知识(十八) 托管和部署 概述

为了方便演示,以 .NET Core 控制台应用程序讲解. 我们新建一个控制台应用程序,安装 "Newtonsoft.Json" Nuget 包,然后右键点击该项目,选择"发布": 我们依次选择"文件",设置好路径,最后点击创建配置文件,界面变成了下面这样: 然后我们点击"配置" 那么,问题来了."部署模式" 里面有两个选项: 1.当选择框架依赖时,"目标运行时"有:"可移植&

ASP.NET Core 2.2 基础知识(十四) WebAPI Action返回类型(未完待续)

要啥自行车,直接看手表 //返回基元类型 public string Get() { return "hello world"; } //返回复杂类型 public Person Get() { return new Person {Id = 1, Name = "refuge"}; } //控制器需要继承 Controller 类 public IActionResult Get() { return Ok("hello world"); }

ASP.NET Core 2.2 基础知识(十六) SignalR (未完待续)

我一直觉得学习的最好方法就是先让程序能够正常运行,才去学习他的原理,剖析他的细节. 就好像这个图: 所以,我们先跟着官方文档,创建一个 SignalR 应用: https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?view=aspnetcore-2.2&tabs=visual-studio 这个例子一共涉及到下面几个步骤: 自定义中心 ChatHub ; 在启动类 Startup 中启用 SignalR 服务,并添加路由;

ASP.NET Core 2.2 基础知识(二) 中间件

原文:ASP.NET Core 2.2 基础知识(二) 中间件 中间件是一种装配到应用管道以处理请求和相应的软件.每个软件都可以: 1.选择是否将请求传递到管道中的下一个组件; 2.可在调用管道中的下一个组件前后执行工作. 管道由 IApplicationBuilder 创建: 每个委托都可以在下一个委托前后执行操作,.此外,委托还可以决定不将请求传递给下一个委托,这就是对请求管道进行短路.通常需要短路,是因为这样可以避免不必要的工作.比如: 1.静态文件中间件可以返回静态文件请求并使管道的其余

ASP.NET Core 2.2 基础知识(一) 依赖注入

原文:ASP.NET Core 2.2 基础知识(一) 依赖注入 依赖: 类A用到了类B,我们就说类A依赖类B.如果一个类没有任何地方使用到,那这个类基本上可以删掉了. public class Test { private MyDependency md = new MyDependency(); public void Print() { md.Print(); } } public class MyDependency { public void Print() { Console.Wri

ASP.NET Core 2.2 基础知识(十一) ASP.NET Core 模块

原文:ASP.NET Core 2.2 基础知识(十一) ASP.NET Core 模块 ASP.NET Core 应用与进程内的 HTTP 服务器实现一起运行.该服务器实现侦听 HTTP 请求,并在一系列请求功能被写到 HttpContext 时,将这些请求展现到应用中. ASP.NET Core 随附两种服务器实现: Kestrel 是适用于 ASP.NET Core 的默认跨平台 HTTP 服务器. HTTP.sys 是仅适用于 Windows 的 HTTP 服务器,它基于 HTTP.sy

ASP.NET Core 2.2 基础知识(六) 配置(内含MySql+EF)

原文:ASP.NET Core 2.2 基础知识(六) 配置(内含MySql+EF) 先上一段代码,了解一下 .NET Core 配置数据的结构. 新建一个 控制台项目,添加一个文件 json.json ,文件内容如下: { "country": "cn", "person": { "id": 1, "address": { "addName": "chengdu"

ASP.NET Core 2.2 基础知识(五) 环境

原文:ASP.NET Core 2.2 基础知识(五) 环境 一.环境变量 系统启动时,会读取环境变量 ASPNETCORE_ENVIRONMENT ,并将该变量的值存储在 IHostingEnvironment.EnvironmentName 字段中.如: 新建一个 WebAPI 项目,修改 Configure 方法: public void Configure(IApplicationBuilder app, IHostingEnvironment env) { ...... { app.R