HttpClientFactory in ASP.NET Core 2.1 Part 2:定义命名和类型化的客户端
HttpClientFactory in ASP.NET Core 2.1 Part 2:定义命名和类型化的客户端
原文地址:https://www.stevejgordon.co.uk/httpclientfactory-named-typed-clients-aspnetcore
上文介绍了 HttpClientFactory。我介绍了在创建功能的内幕,介绍了它帮助解决的问题,然后使用一个很基本的示例展示了如何在 WebAPI 应用中使用。本文我希望深入介绍使用它的两种方式:
- 命名客户端
- 类型化客户端
命名客户端
上文中,我介绍了如何使用 HttpClientFactory 来获取一的基础的 HttpClient 实例。当我们只是简单进行一次请求的时候也是可以的。但是,我们经常需要从代码的多个位置,多次访问同样的服务。
HttpClientFactory 通过命名客户端的方式使得更容易做到这一点。你可以创建一个包含一些特定配置的用来应用到创建 HttpClient 时的注册。你还可以注册多个命名客户端,它们使用不同的配置。
为了更具体一点。下面看一个示例,在 Startup 文件的 ConfigureServices() 方法中,我将使用 AddHttpClient() 扩展方法的另外重载方式,它接受两个附加的参数,一个名字和一个参数为 HttpClient 的 Action 委托,我的 ConfigureServices() 方法如下所示。
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("GitHubClient", client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting");
});
services.AddMvc();
}
第一个参数是注册客户端的名称。Action<HttpClient> 委托支持我们在它构建的时候,配置 HttpClient 对象,它很顺手,因为可以预定义基本的访问地址,和一些已知的请求头,例如,当我们请求命名客户端的使用,每次新的实例将被创建,并如我们希望进行配置。
为使用它,在调用 CreateClient() 的时候,我们可以通过名称来请求客户端。
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly IHttpClientFactory _httpClientFactory;
public ValuesController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet]
public async Task<ActionResult> Get()
{
var client = _httpClientFactory.CreateClient("GitHubClient");
var result = await client.GetStringAsync("/");
return Ok(result);
}
}
在示例中,我们可以得到一个已经配置了基础访问地址的实例,所以在 GetStringAsync() 方法中,可以仅仅提供相对于基础地址的 URI 。
命名的方式在应用到客户端的配置之上给予一些控制,我不是对魔术字符串有很大兴趣,所以,对于命名客户端,我使用静态类包含字符串常量的方式。如下所示:
public static class NamedHttpClients
{
public const string GitHubClient = "GitHubClient";
}
在注册或者请求 HttpClient 的时候,我们可以使用静态类的值,而不是魔术字符串。
services.AddHttpClient(NamedHttpClients.GitHubClient, client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting");
});
这样已经很好了,但是,我们可以进一步使用定制的类型化客户端。
类型化客户端
类型化的客户端允许我们使用定制的类,它可以构造函数注入到期待 HttpClient 的类中。它可以通过依赖注入系统,使用在 IHttpClientBuilder 上的扩展方法来封装,或者使用范型的 AddClientHttp() 来接受定制的类型。一旦我们拥有了自己定制的类型,我们既可以直接暴露出 HttpClient,或者在特定的方法中封装 Http 调用,更好地定义对外部服务的调用。该方式还意味着,我们不再需要魔术字符串,看起来更有意义。
我们看一个基本的示例,从我们定义定制的类型化 HttpClient 开始。
public class MyGitHubClient
{
public MyGitHubClient(HttpClient client)
{
Client = client;
}
public HttpClient Client { get; }
}
该类型需要接受一个 HttpClient 作为构造函数的参数。现在,我们设置一个公共属性来持有该 HttpClient 实例。
然后,需要在 ConfigureServices() 中如下进行服务注册:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<MyGitHubClient>(client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting");
});
services.AddMvc();
}
通过将自定义类型 MyGitHubClient 类型作为范型参数传递给 AddHttpClient(),这将注册以瞬态作用域注册到容器中。由于我们的定制类型接受一个 HttpClient,它将使用适当的 HttpClient 注入到它中,在工厂中被创建。现在,我们可以更新控制器来接收我们类型化的客户端而不是 IHttpClientFactory 工厂。
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly MyGitHubClient _gitHubClient;
public ValuesController(MyGitHubClient gitGitHubClient)
{
_gitHubClient = gitGitHubClient;
}
[HttpGet]
public async Task<ActionResult> Get()
{
var result = await _gitHubClient.Client.GetStringAsync("/");
return Ok(result);
}
}
由于我们定制的类型化客户端也通过属性暴露了 HttpClient,所以也可以直接使用它发出 Http 请求。
封装 HttpClient
在最终的示例中,我们专注于完全封装 HttpClient。该方式主要用于在我们希望在定义的方法中处理对端点的请求。此时,我们还希望能够封装对响应的验证,反序列化。所以希望在单个位置中的每个方法进行处理。
在此种情况下,我们保存在构造函数中注入的 HttpClient 到私有的只读字段中,与直接接收 HttpClient 相反,我们提供了 GetRootDataLength() 方法来执行 Http 调用,并返回响应的长度。通过简单的示例来使你了解思路。
public interface IMyGitHubClient
{
Task<int> GetRootDataLength();
}
public class MyGitHubClient : IMyGitHubClient
{
private readonly HttpClient _client;
public MyGitHubClient(HttpClient client)
{
_client = client;
}
public async Task<int> GetRootDataLength()
{
var data = await _client.GetStringAsync("/");
return data.Length;
}
}
我还更新了类型化客户端,使其从接口实现。我们可以更新控制器来接收并消费该接口。
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly IMyGitHubClient _gitHubClient;
public ValuesController(IMyGitHubClient gitHubClient)
{
_gitHubClient = gitHubClient;
}
[HttpGet]
public async Task<ActionResult> Get()
{
var result = await _gitHubClient.GetRootDataLength();
return Ok(result);
}
}
现在我们可以根据接口的定义来调用 GetRootDataLength() 方法,不再需要直接操作 HttpClient,真正的闪光点在测试,在我们希望测试控制器的时候,我们可以模拟我们的 IMyGithubClient,测试 HttpClient 在以前是一个痛点,需要多行代码,通常我需要一套模拟。
在 ConfigureServices() 方法中注册到容器中。
services.AddHttpClient<IMyGitHubClient, MyGitHubClient>(client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting");
});
AddHttpClient() 有一个可以接受两个范型参数的签名,并以适当的方式组织到容器中。
生成的客户端
IHttpClientFactory
可结合第三方库(例如 Refit)使用。 Refit 是.NET 的 REST 库。 它将 REST API 转换为实时接口。 RestService
动态生成该接口的实现,使用 HttpClient
进行外部 HTTP 调用。
定义了接口和答复来代表外部 API 及其响应:
C#复制
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
可以添加类型化客户端,使用 Refit 生成实现:
C#复制
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("hello", c =>
{
c.BaseAddress = new Uri("http://localhost:5000");
})
.AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));
services.AddControllers();
}
可以在必要时使用定义的接口,以及由 DI 和 Refit 提供的实现:
C#复制
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHelloClient _client;
public ValuesController(IHelloClient client)
{
_client = client;
}
[HttpGet("/")]
public async Task<ActionResult<Reply>> Index()
{
return await _client.GetMessageAsync();
}
}
总结
本文介绍了一些更高级的我们使用 HttpClientFactory 的方式,支持我们创建不同种类的命名配置的 HttpClient 实例。我们可以使用类型化的 HttpClient,通过扩展可以支持实现自定义的类,它接受一个 HttpClient 实例作为构造函数参数,还可以使用生成的 HttpClient 。
Part 1 – HttpClientFactory in ASP.NET Core 2.1 Part 1 介绍
Part 2 – HttpClientFactory in ASP.NET Core 2.1 Part 2:定义命名和类型化的客户端
Part 3 – HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件
Part 4 – HttpClientFacotry Part 4: 集成 Polly 处理瞬时失效
Part 5 – HttpClientFactory in ASP.NET Core 2.1 Part 5: 日志
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2011-03-05 使用 jQuery dataTables - 2 四种数据来源