asp.net HTTP客户端工厂:具有附加构造函数参数的类型化HttpClient

6ljaweal  于 2022-11-19  发布在  .NET
关注(0)|答案(6)|浏览(129)

使用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);
    }
}

然后在Startup.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);
    }
}

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

ycggw6v2

ycggw6v21#

我设法通过切换到命名客户端来实现这一点:

//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);
});
pprl5pva

pprl5pva2#

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

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

注册新类

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

然后注册HttpClient

services.AddHttpClient<IGitHubService, GitHubService>();

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

3xiyfsfu

3xiyfsfu3#

也许你可以通过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");
});
46scxncf

46scxncf4#

如果您使用Autofac作为依赖关系解析器,则可以使用它的Delegate Factories来实现此目的。
它看起来像这样
第一个
据我所知,这是不可能与内置的依赖关系解析器。

waxmsbnn

waxmsbnn5#

实际上有一个足够简单的方法可以一步完成。

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

gc0ot86w6#

正确的方法是使用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)
);

相关问题