HttpClientFactory:带有附加构造函数参数的类型化 HttpClient



使用 HttpClientFactory,我们可以配置依赖注入来创建和管理 HttpClient 的生命周期:

public class GitHubService : IGitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
client.BaseAddress = new Uri("https://api.github.com/");
// GitHub API versioning
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
client.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
Client = client;
}
public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
{
var response = await Client.GetAsync(
"/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubIssue>>(responseStream);
}
}

然后在启动中.cs我们配置 DI:

services.AddHttpClient<GitHubService>();

但是,如果类型化客户端具有其他构造函数参数,则应如何提供这些参数? 例如,如果要传入存储库名称:

public class GitHubService : IGitHubService
{
public HttpClient Client { get; }
private readonly string _repositoryName;
public GitHubService(HttpClient client, string repositoryName)
{
_repositoryName = repositoryName;
client.BaseAddress = new Uri("https://api.github.com/");
// GitHub API versioning
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
client.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
Client = client;
}
public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
{
var response = await Client.GetAsync(
$"/repos/aspnet/{_repositoryName}/issues?state=open&sort=created&direction=desc");
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubIssue>>(responseStream);
}
}

也许这不是一个现实的例子,但是如何配置依赖注入以提供存储库名称?

我设法通过切换到命名客户端来使其工作:

//To start with, create a named client:
services.AddHttpClient("GitHubClient", ctx => { ctx.BaseAddress = new Uri("https://api.github.com/"); });
//Then set up DI for the TypedClient
services.AddTransient<IGitHubService>(ctx =>
{
var clientFactory = ctx.GetRequiredService<IHttpClientFactory>();
var httpClient = clientFactory.CreateClient("GitHubClient");

return new GitHubService(httpClient, repositoryName);
});

如果不想使用命名客户端,另一种方法是为额外参数创建自定义类。 因为问题是它不知道要为字符串类型解析什么,所以您可以做的是创建一个具有字符串属性的对象,该字符串属性将包含要传递的值,将其注册为单一实例并让容器解析它。

创建一个包含所有额外参数的类。在您的情况下"存储库名称">

public class RepositoryConfig
{
public string RepositoryName {get; set;}
}

注册新类

services.AddSingleton(new RepositoryConfig { RepositoryName = "MyRepo"});

然后注册 HttpClient

services.AddHttpClient<IGitHubService, GitHubService>();

现在您的类将被正确实例化。

实际上有一种足够简单的方法可以在一个步骤中做到这一点。

services.AddHttpClient<IGitHubService, GitHubService>((client, sp) =>
{
client.BaseAddress = new Uri("https://api.github.com/");
return new GitHubService(client, repositoryName);
});

也许您可以通过HttpClient的属性传递参数。存储库名称可以通过BaseAddress传递。

var repositoryName = // load from a config for example
services.AddHttpClient<GitHubService>(c =>
{
c.BaseAddress = new Uri($"https://api.github.com/repos/aspnet/{repositoryName}");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

如果您使用 Autofac 作为依赖项解析程序,则可以使用它的委托工厂来实现这一点。

它看起来像这样

public delegate GitHubService GitHubServiceFactory(string repositoryName);
public class AnotherService
{
private GitHubService gitHubService;
public AnotherService(GitHubServiceFactory gitHubServiceFactory)
{
this.gitHubService = gitHubServiceFactory("myRepositoryName");
}
}

据我所知,使用内置依赖项解析器无法做到这一点。

正确的方法是使用AddHttpClient<TClient,TImplementation>(Func<HttpClient, IServiceProvider, TImplementation>)扩展方法:

services.AddHttpClient<IGitHubService, GitHubService>((client, sp) =>
// any other constructor dependencies in GitHubService will be filled in
// by ActivatorUtilities from the provided IServiceProvider
ActivatorUtilities.CreateInstance<GitHubService>(sp, client, repositoryName)
);

最新更新