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

phi*_*eed 8 c# asp.net asp.net-web-api asp.net-core

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

services.AddHttpClient<GitHubService>();
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

phi*_*eed 10

我设法通过切换到命名客户端来完成这项工作:

//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);
});
  
Run Code Online (Sandbox Code Playgroud)

  • 这应该是瞬态的还是单例的? (3认同)

Phi*_*ter 9

实际上有一种足够简单的方法可以一步完成此操作。

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


Car*_*soj 8

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

创建一个包含所有额外参数的类。在你的情况下“repositoryName”

public class RepositoryConfig
{
    public string RepositoryName {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

注册新班级

services.AddSingleton(new RepositoryConfig { RepositoryName = "MyRepo"});
Run Code Online (Sandbox Code Playgroud)

然后注册HttpClient

services.AddHttpClient<IGitHubService, GitHubService>();
Run Code Online (Sandbox Code Playgroud)

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