问题描述
在我看到的每个示例中,包括Microsoft的here和here,作者将解释IHttpClientFactory
对HttpClient
的改进,并举例说明可以直接使用它,也可以以命名形式使用它。但是随后所有人似乎都提到,使用Typed表单确实对它的结构,可用性等而言是最好的。原因对我们的用例来说很有意义。
尽管与上面提供的链接一样,在创建类型IHttpClientFactory
(或作为客户端的服务)的过程中,没有一行代码实例化,注入或使用HttpClient
。您创建类型客户端:
public class GitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
,然后在某些模型或控制器中使用它:
public TypedClientModel(GitHubService gitHubService)
{
_gitHubService = gitHubService;
}
public async Task OnGet()
{
try
{
LatestIssues = await _gitHubService.GetAspNetDocsIssues();
}
我非常困惑。我的团队最初遇到了障碍,试图使用Moq对类型化的客户进行模拟以进行单元测试,而得出的结论是,在获得大量宝贵资源后,我们发现IHttpClientFactory
使得模拟变得更容易了。但是我还没有找到一个示例,该示例明确将IHttpClientFactory
与类型客户端一起使用。
解决方法
框架将使用ITypedHttpClientFactory
创建HttpClient
,以注入到类型化的客户端中。当像这样配置类型化的客户端时,这是在后台进行的:
services.AddHttpClient<ICatalogService,CatalogService>()
如果我们窥视AddHttpClient
,我们会发现它会尝试创建名为IHttpClientFactory
的{{1}}的临时版本
ITypedHttpClientFactory
类型化的客户端也允许抽象的客户端
services.TryAdd(ServiceDescriptor.Transient(typeof(ITypedHttpClientFactory<>),typeof(DefaultTypedHttpClientFactory<>)));
在接口及其实现中注册的位置
public class GitHubService :IGitHubService { // <-- NOTE THE INTERFACE
HttpClient client
public GitHubService(HttpClient client) {
this.client = client;
}
//...
并相应地使用
services.AddHttpClient<IGitHubService,GitHubService>();
这样做的好处是,您可以脱离第三方的依赖关系(框架问题),因为您可以控制类型化的客户端及其抽象。
这将允许在隔离测试时更轻松地模拟类型化的客户端抽象。
,使用IHttpClientFactory
,您有三个选择:
IHttpClientFactory
用法
public class SampleController
{
private readonly IHttpClientFactory _clientFactory;
public SampleController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
}
嘲笑
//Arrange
var mockClientFactory = new Mock<IHttpClientFactory>();
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync",ItExpr.IsAny<HttpRequestMessage>(),ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(expectedResponseMessage);
var client = new HttpClient(mockMessageHandler.Object);
mockClientFactory
.Setup(_ => _.CreateClient(It.IsAny<string>()))
.Returns(client);
指定客户
用法
public class SampleController
{
private readonly HttpClient _client;
public SampleController(IHttpClientFactory clientFactory)
{
_client = clientFactory.CreateClient("SampleProxy");
}
}
嘲笑
作为alternative,我们可以使用自定义HttpMessageHandler
public class FakeMessageHandler: HttpMessageHandler
{
public virtual HttpResponseMessage Send(HttpRequestMessage request)
{
throw new NotImplementedException();
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)
{
return Task.FromResult(Send(request));
}
}
//Arrange
var mockClientFactory = new Mock<IHttpClientFactory>();
var mockMessageHandler = new Mock<FakeMessageHandler> { CallBase = true };
mockMessageHandler
.Setup(handler => handler.Send(It.IsAny<HttpRequestMessage>()))
.Returns(expectedResponseMessage);
var client = new HttpClient(mockMessageHandler.Object);
mockClientFactory
.Setup(_ => _.CreateClient("SampleProxy"))
.Returns(client);
键入的客户端
用法
public class SampleController
{
private readonly ISampleClient _client;
public SampleController(ISampleClient client)
{
_client = client;
}
}
嘲笑
//Arrange
var clientMock = new Mock<ISampleClient>();
clientMock
.Setup(client => client.GetXYZ(It.IsAny<SampleRequest>()))
.ReturnsAsync(expectedSampleResponse);
var SUT = new SampleController(clientMock.Object);